From patchwork Fri Feb 28 00:26:01 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Tobler X-Patchwork-Id: 13995375 Received: from mail-oo1-f49.google.com (mail-oo1-f49.google.com [209.85.161.49]) (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 DDC5E2595 for ; Fri, 28 Feb 2025 00:29:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740702569; cv=none; b=OMJUkfAsxW2VwaGvnwmlXfpDnV5mpuJVhbv5FHTyiyN6av6d1NBwSt5JPGaNDRbsH0HpAp9MvxB2IH47juJZvjtm6jBY8t78upxAN1q9fEtSkiy7qE1+xUnDQTFW5Wf9MwVYcJMJM0LSeZG6Genmq4C87yF/szYimK66ShYtBbM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740702569; c=relaxed/simple; bh=1J2x39s0XLh1sCWMM94XZ9BOn905I2s5UyrOQYTlPWQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ti2+fbm/Sx2/S/xZ8TTmL7TrebmB8jOugyix4/TMa66MSpx3m55aRLfC8eXZyEV5OVCm3znZkK+LMIKRICxNTe2Wwfk0te0qIrZ+MFNrFHfAtDMPNk4K7rIcmJZYS2uROGl7QABgZ5Zx02ywVrvh3a6oV9sDfpXO6FNYUYvpOmI= 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=Pf0Q0pWI; arc=none smtp.client-ip=209.85.161.49 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="Pf0Q0pWI" Received: by mail-oo1-f49.google.com with SMTP id 006d021491bc7-5fcd56c34a1so868457eaf.0 for ; Thu, 27 Feb 2025 16:29:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1740702566; x=1741307366; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=L2S1IpkDcboFUmNqoGg63vclsReOu00DdzecLlPSqMs=; b=Pf0Q0pWIDgOa5jc84zn4C7nRcAZt32SgIbRwG6pYZzvP29e50SGqReGR2hM6pFN0XV zj9buX46Er3Xrp50zfCv/ZkowshULlekFPUjFqT1gi6fylUapLrHhCkuhjM4N1hAZf1Z zAqTKZkNMsNOiXXbkxnG91ltMZI1/906GoazQpmT3OhQrozCg2quB1T3Kb0Iz50fPopR K5TpNk2BU+EjoJRRMonR78ueTDBjlH1dEIwo2bLAAEEtPQ81VzMxjAXHR5iUwbPH91zK Iduzfai2Q+cUxDVYGnNCEPmy82um5KzkwHP4FjBmAJ1LdpiNPtNQZZB7LY4+1rM4buaF OpQA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740702566; x=1741307366; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=L2S1IpkDcboFUmNqoGg63vclsReOu00DdzecLlPSqMs=; b=ha0LFyNKa7gI/9uatfVr8U8o7F42bF+5DMZc6sVPCWV3FxUFSsJE6p9eoYYDoZxtQf ckUi7OtXkoE9d5vrugnf69xhpvMygdn4YNOAfXXfZ8AXgwnTfsb/ZeUUaVaCmpyi4vJo XouKZeTnb4Qh8gJDDIvgMoXGcebQ1/FGpkbIvwAp3ZOwKSopstOFYaviZ0xhfnX4r2fg DQsdfQyPtsQbB8IYNS/vn2PWXlg/vhzwL+IOcwYQugZ8Z0jz2KNxjMZVz377unHaUZr6 wkNNljUX83MbR1JA0GRMAp6YbJJA2IaFuNLHHk7B6QbLz7b9zxlelKA4c/Ca0+ZUnjE/ t+mQ== X-Gm-Message-State: AOJu0Yz+9iNhQLf8Sr4EV3a+E/dZWIvLicFsPd7/mKXMlsSobaNnty8H dvHU7NTjXu2/AXt+1CHZBOTHC9oktG7v01TImTalJZTo+IoxbytLfkNOCTAE X-Gm-Gg: ASbGncumHVRhTK9bLSMhW8bLOaDiFwh/nbmn1p9B76sKCiRLuC/lT44ISysn3ucUQth uEOMd7OyF5aFiB5JY6qDy7SMM6drdVmExr5aptw71Q4rrPDjmN+JhjDv7lR7YhByDlKYKQFnbnX sgQY16mu7nWPV0OLy0RvLiBncRwxfDJDGIrKS6u4BQ0bLEFho7mWmpHrlaJp4SiymyDMcRDF0Or scpg543+rBgrmv1SXG/2cQSbjJvWxwXOTVeRgJbls0tIRFxFbIEud0oXs5QFS15uPKC2yAZatqR y3J7/dLdR/hrw0feHbJLtLX2xtVKKTmCtA== X-Google-Smtp-Source: AGHT+IHcuh5lT37zPVYPH18xah0ZMgW3AQRpzM2VrQDAESi1XK/M2OlbD7XtCudaChNy926AufR1MA== X-Received: by 2002:a05:6820:270a:b0:5fc:fb1a:2b92 with SMTP id 006d021491bc7-5feb361493fmr645898eaf.6.1740702566322; Thu, 27 Feb 2025 16:29:26 -0800 (PST) Received: from denethor.localdomain ([136.50.74.45]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-5feaab9c8f9sm456509eaf.19.2025.02.27.16.29.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Feb 2025 16:29:25 -0800 (PST) From: Justin Tobler To: git@vger.kernel.org Cc: ps@pks.im, karthik.188@gmail.com, phillip.wood123@gmail.com, Justin Tobler Subject: [PATCH v4 1/4] diff: return diff_filepair from diff queue helpers Date: Thu, 27 Feb 2025 18:26:01 -0600 Message-ID: <20250228002604.3859939-2-jltobler@gmail.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250228002604.3859939-1-jltobler@gmail.com> References: <20250225233925.1345086-1-jltobler@gmail.com> <20250228002604.3859939-1-jltobler@gmail.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The `diff_addremove()` and `diff_change()` functions set up and queue diffs, but do not return the `diff_filepair` added to the queue. In a subsequent commit, modifications to `diff_filepair` need to occur in certain cases after being queued. Since the existing `diff_addremove()` and `diff_change()` are also used for callbacks in `diff_options` as types `add_remove_fn_t` and `change_fn_t`, modifying the existing function signatures requires further changes. The diff options for pruning use `file_add_remove()` and `file_change()` where file pairs do not even get queued. Thus, separate functions are implemented instead. Split out the queuing operations into `diff_queue_addremove()` and `diff_queue_change()` which also return a handle to the queued `diff_filepair`. Both `diff_addremove()` and `diff_change()` are reimplemented as thin wrappers around the new functions. Signed-off-by: Justin Tobler --- diff.c | 70 +++++++++++++++++++++++++++++++++++++++++----------------- diff.h | 25 +++++++++++++++++++++ 2 files changed, 75 insertions(+), 20 deletions(-) diff --git a/diff.c b/diff.c index 019fb893a7..b5a779f997 100644 --- a/diff.c +++ b/diff.c @@ -7157,16 +7157,19 @@ void compute_diffstat(struct diff_options *options, options->found_changes = !!diffstat->nr; } -void diff_addremove(struct diff_options *options, - int addremove, unsigned mode, - const struct object_id *oid, - int oid_valid, - const char *concatpath, unsigned dirty_submodule) +struct diff_filepair *diff_queue_addremove(struct diff_queue_struct *queue, + struct diff_options *options, + int addremove, unsigned mode, + const struct object_id *oid, + int oid_valid, + const char *concatpath, + unsigned dirty_submodule) { struct diff_filespec *one, *two; + struct diff_filepair *pair; if (S_ISGITLINK(mode) && is_submodule_ignored(concatpath, options)) - return; + return NULL; /* This may look odd, but it is a preparation for * feeding "there are unchanged files which should @@ -7186,7 +7189,7 @@ void diff_addremove(struct diff_options *options, if (options->prefix && strncmp(concatpath, options->prefix, options->prefix_length)) - return; + return NULL; one = alloc_filespec(concatpath); two = alloc_filespec(concatpath); @@ -7198,25 +7201,29 @@ void diff_addremove(struct diff_options *options, two->dirty_submodule = dirty_submodule; } - diff_queue(&diff_queued_diff, one, two); + pair = diff_queue(queue, one, two); if (!options->flags.diff_from_contents) options->flags.has_changes = 1; + + return pair; } -void diff_change(struct diff_options *options, - unsigned old_mode, unsigned new_mode, - const struct object_id *old_oid, - const struct object_id *new_oid, - int old_oid_valid, int new_oid_valid, - const char *concatpath, - unsigned old_dirty_submodule, unsigned new_dirty_submodule) +struct diff_filepair *diff_queue_change(struct diff_queue_struct *queue, + struct diff_options *options, + unsigned old_mode, unsigned new_mode, + const struct object_id *old_oid, + const struct object_id *new_oid, + int old_oid_valid, int new_oid_valid, + const char *concatpath, + unsigned old_dirty_submodule, + unsigned new_dirty_submodule) { struct diff_filespec *one, *two; struct diff_filepair *p; if (S_ISGITLINK(old_mode) && S_ISGITLINK(new_mode) && is_submodule_ignored(concatpath, options)) - return; + return NULL; if (options->flags.reverse_diff) { SWAP(old_mode, new_mode); @@ -7227,7 +7234,7 @@ void diff_change(struct diff_options *options, if (options->prefix && strncmp(concatpath, options->prefix, options->prefix_length)) - return; + return NULL; one = alloc_filespec(concatpath); two = alloc_filespec(concatpath); @@ -7235,19 +7242,42 @@ void diff_change(struct diff_options *options, fill_filespec(two, new_oid, new_oid_valid, new_mode); one->dirty_submodule = old_dirty_submodule; two->dirty_submodule = new_dirty_submodule; - p = diff_queue(&diff_queued_diff, one, two); + p = diff_queue(queue, one, two); if (options->flags.diff_from_contents) - return; + return p; if (options->flags.quick && options->skip_stat_unmatch && !diff_filespec_check_stat_unmatch(options->repo, p)) { diff_free_filespec_data(p->one); diff_free_filespec_data(p->two); - return; + return p; } options->flags.has_changes = 1; + + return p; +} + +void diff_addremove(struct diff_options *options, int addremove, unsigned mode, + const struct object_id *oid, int oid_valid, + const char *concatpath, unsigned dirty_submodule) +{ + diff_queue_addremove(&diff_queued_diff, options, addremove, mode, oid, + oid_valid, concatpath, dirty_submodule); +} + +void diff_change(struct diff_options *options, + unsigned old_mode, unsigned new_mode, + const struct object_id *old_oid, + const struct object_id *new_oid, + int old_oid_valid, int new_oid_valid, + const char *concatpath, + unsigned old_dirty_submodule, unsigned new_dirty_submodule) +{ + diff_queue_change(&diff_queued_diff, options, old_mode, new_mode, + old_oid, new_oid, old_oid_valid, new_oid_valid, + concatpath, old_dirty_submodule, new_dirty_submodule); } struct diff_filepair *diff_unmerge(struct diff_options *options, const char *path) diff --git a/diff.h b/diff.h index 0a566f5531..63afa17e84 100644 --- a/diff.h +++ b/diff.h @@ -508,6 +508,31 @@ void diff_set_default_prefix(struct diff_options *options); int diff_can_quit_early(struct diff_options *); +/* + * Stages changes in the provided diff queue for file additions and deletions. + * If a file pair gets queued, it is returned. + */ +struct diff_filepair *diff_queue_addremove(struct diff_queue_struct *queue, + struct diff_options *, + int addremove, unsigned mode, + const struct object_id *oid, + int oid_valid, const char *fullpath, + unsigned dirty_submodule); + +/* + * Stages changes in the provided diff queue for file modifications. + * If a file pair gets queued, it is returned. + */ +struct diff_filepair *diff_queue_change(struct diff_queue_struct *queue, + struct diff_options *, + unsigned mode1, unsigned mode2, + const struct object_id *old_oid, + const struct object_id *new_oid, + int old_oid_valid, int new_oid_valid, + const char *fullpath, + unsigned dirty_submodule1, + unsigned dirty_submodule2); + void diff_addremove(struct diff_options *, int addremove, unsigned mode, From patchwork Fri Feb 28 00:26:02 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Tobler X-Patchwork-Id: 13995376 Received: from mail-ot1-f54.google.com (mail-ot1-f54.google.com [209.85.210.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 177BEAD2C for ; Fri, 28 Feb 2025 00:29:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740702570; cv=none; b=uW06i9aNy/+13hW/Hk51aPNCezNeGoPtS+/W07dsAXuNmMxtQpz7Gx2Fwd6xTECIWC5QwBBfS7WmTS1nfZMwdB9MTVZCHOCOrYetdBh9rwh+HJkQqu4gv6UmMNgmp72eaJ4TIOiqDRbbcn7V+VdEUtmqhYZppp0Z3rmZT+gRvZE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740702570; c=relaxed/simple; bh=O+hAY1pofdpNaFonVJKAOOz3Dsk/jJc6qA1SokieYkw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=MIGKaPqRmvgZYO550kD6EJOttBlBX/nMbWz6AP4VAEXN4sNio5PuQfb1Pj8R9IfmOMGF1NOv08rBreUFoQbmKlyjdxliHATXVgoAg4PA8qQO51U9Co2LjaB+8zsoRDDHXt9xn/3rE0q4x/Fr1iQGj/5rChKVMW++ColjrCYjgEo= 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=bphzsKuB; arc=none smtp.client-ip=209.85.210.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="bphzsKuB" Received: by mail-ot1-f54.google.com with SMTP id 46e09a7af769-728a433ec30so1046355a34.1 for ; Thu, 27 Feb 2025 16:29:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1740702567; x=1741307367; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=k2YhYqgSveRgqexJCRWiA7ZeJ+THWdG09xVL+TUp5pg=; b=bphzsKuBDEwYIQxh2YJmdHKTXbej8/xFvH/UwJo5/J9tNhBxgFd/J+woO4Ww1IYbeH 6T8Itk31otAKE3lbw1HDiXJLd2DkeaIjLG34O5TeTxlZ/EzAe4xuMTvKQkhTBgVAHejz KdH4n63HBTty0cpFiZaIowAYV9SiPmGdEjxkThpSZrumaKDfZWpGC6Z4y/OYMdPqeK33 y//HbXg1H5684M6TD82aGYx5o/aEUNqTd4PdCywKkFVEGOHl80aM6DfWcTPEY7f+Vi4b cPeBzxljy40PwSS7B9x9AP5tjixhAIQkkDVt25FHhH0+agkaNFotBv7XDDl9Icf4Ni96 r/OQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740702567; x=1741307367; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=k2YhYqgSveRgqexJCRWiA7ZeJ+THWdG09xVL+TUp5pg=; b=LuGvElp/4W6GiZd0sUsMTyS7cOd5LVqIdTYg7SNAo/ctZ2XN2N3TaA7ZUOQVu0/7eZ sRc8w4VexM5Ry4aud4roFFP5hmhDWxbJQGimY3F8f7arGrCk3lDbGVO/nFcHaRnTYwWo 9soKrSpK4iHl0EW31YR0VGghWJ83gG38DpJi8sbckljzgYDH2/E5qAhsD8zihCjtTO6j UBbhF1+PJP3mKj86OyinW+A9noB1eJ3wFCHWq2gDsHgWU2NLOA1SOSP8jiBaJ1qsJ46X GXWcvVmR8fn4GoT1Orp27slW6h76j7HSpD2EMMSuvWWz5/sLbfoKo/ChXca/XlWS4gKw YoMg== X-Gm-Message-State: AOJu0YwfSAwd6hogJ3ORbQL4CSBGYI74MXztNrUTODRECSXZxKDACMIL lPkqbcxavmWDB2uGf+rNJeHNAUm0MICiQ9JWUrDGCCZknruzvkybT7ySzXsH X-Gm-Gg: ASbGncsjfbHdXQbXRxGZlAuxrRW++kGDbTAsTUQP8AZkoZHydo7t5Xdu7LhXmWnJRZK tLoXyfp7bRYgT8zDdzR30AYbsK2VeOUle0ys1Lkd++CxxE/A3huB+kvjdH2p5PkRLBSr7Ds7mK7 fMkU2bijMGp0dwG2tngy7Ubi2BqLcVOFsjNS4ynAz/HXSOHZ3tyPIEB192QjrWhHU1a0+36BJ+Y UFO6W5gDLpXjPDctIeVHsK7iTVdPSC4Os1kGrqFd/fyVJDrotNtO4nSqckniuVjVNU5rvjBMU7D 6yU3VjbsZAHeAepcrTbJ6RAiNU7LPgfYCA== X-Google-Smtp-Source: AGHT+IGTlC0EW+jMA8/iIzUHYUxj6sHxED1pzE1GMbd3bkE2Hvo1XQWoZrZjxuVMoAWscHAqyWxJ2w== X-Received: by 2002:a05:6830:7302:b0:727:3313:5634 with SMTP id 46e09a7af769-728b8307c9amr818947a34.27.1740702567586; Thu, 27 Feb 2025 16:29:27 -0800 (PST) Received: from denethor.localdomain ([136.50.74.45]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-5feaab9c8f9sm456509eaf.19.2025.02.27.16.29.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Feb 2025 16:29:26 -0800 (PST) From: Justin Tobler To: git@vger.kernel.org Cc: ps@pks.im, karthik.188@gmail.com, phillip.wood123@gmail.com, Justin Tobler Subject: [PATCH v4 2/4] diff: add option to skip resolving diff statuses Date: Thu, 27 Feb 2025 18:26:02 -0600 Message-ID: <20250228002604.3859939-3-jltobler@gmail.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250228002604.3859939-1-jltobler@gmail.com> References: <20250225233925.1345086-1-jltobler@gmail.com> <20250228002604.3859939-1-jltobler@gmail.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 By default, `diffcore_std()` resolves the statuses for queued diff file pairs by calling `diff_resolve_rename_copy()`. If status information is already manually set, invoking `diffcore_std()` may change the status value. Introduce the `skip_resolving_statuses` diff option that prevents `diffcore_std()` from resolving file pair statuses when enabled. Signed-off-by: Justin Tobler --- diff.c | 2 +- diff.h | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/diff.c b/diff.c index b5a779f997..37cc88c75b 100644 --- a/diff.c +++ b/diff.c @@ -7081,7 +7081,7 @@ void diffcore_std(struct diff_options *options) diffcore_order(options->orderfile); if (options->rotate_to) diffcore_rotate(options); - if (!options->found_follow) + if (!options->found_follow && !options->skip_resolving_statuses) /* See try_to_follow_renames() in tree-diff.c */ diff_resolve_rename_copy(); diffcore_apply_filter(options); diff --git a/diff.h b/diff.h index 63afa17e84..fc791ee2cc 100644 --- a/diff.h +++ b/diff.h @@ -353,6 +353,14 @@ struct diff_options { /* to support internal diff recursion by --follow hack*/ int found_follow; + /* + * By default, diffcore_std() resolves the statuses for queued diff file + * pairs by calling diff_resolve_rename_copy(). If status information + * has already been manually set, this option prevents diffcore_std() + * from resetting statuses. + */ + int skip_resolving_statuses; + /* Callback which allows tweaking the options in diff_setup_done(). */ void (*set_default)(struct diff_options *); From patchwork Fri Feb 28 00:26:03 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Tobler X-Patchwork-Id: 13995377 Received: from mail-oi1-f172.google.com (mail-oi1-f172.google.com [209.85.167.172]) (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 95598224CC for ; Fri, 28 Feb 2025 00:29:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740702572; cv=none; b=mAyUXCTsCfCwov+cucJ4K93nBhEwguKAdZr3D7llDqe7PcZzN4l69WdKQEAO2Wkjdp98MDbHTkvpngzgfHSjNAZ61lv7AfCs4h+HyOCH+BupwhXikwKAXH82dVy65dSr7pRVJD5kTZjDy6sga7uF9jkBVo4+ztEnOAt9yvmjAAA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740702572; c=relaxed/simple; bh=1MJ0dF0GjmgIP3kscDYMLwydDVI8BBccVBHfcqBSkzY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=GrSDhcvRYVRNYo88Pgui4d32qRU2MnuaGaoRQNLECgAnWdDP4/vD9kQYjV2RAw3t+TRf1NTpL26NeWc3hnWMvUA5zowIIf5OUaAkY/Oh/Y/abamADMxj+rdqWU/UV6JGp7t1ycjY59/vRtAILVZjJwQ4L+tg0msQWmZilHJlx6Q= 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=Rv1WEMFO; arc=none smtp.client-ip=209.85.167.172 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="Rv1WEMFO" Received: by mail-oi1-f172.google.com with SMTP id 5614622812f47-3f556b90790so210455b6e.0 for ; Thu, 27 Feb 2025 16:29:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1740702569; x=1741307369; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=w42z4KiXPqFgWiPV9UBFxNtFqEU2+tNWzlQU+mYZrEc=; b=Rv1WEMFOR6egPLsiSAapgIB4p83Ks7SrsfFCLWvHfaYAx3BFyr2Zko2H351EPNhRmh dJvL76Z+EhnCNLn8iIaz60KaVHwMS+8Gp6Yx1VgHb8AItzMIyvFANChID3jwZiSLwAIZ 2K6Z+S1czIB1ZpPgd4CzgSagRsPiKd+nUhO5vjluGkXH4CkDyQhPVk53rHKtIqeWyHC7 hspemGxaoJk0wiD9kMNK/wf/ZiMm0QBXN9k5xYTpibDrVSL7ZAhMhQ8Lybw2PoCIEwJu eycPkozmiUbpN6aT9A926jK6eIULvC+06W2mjANYqGWlfn+2GBo7vLyq1i83d6v9/UQh rRTw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740702569; x=1741307369; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=w42z4KiXPqFgWiPV9UBFxNtFqEU2+tNWzlQU+mYZrEc=; b=MRpSsGgHTM6/cUw+Puz2MwE2PaQJXy8OuCN6t0jH80d0bMUDxtuXOhb5QH6S/g5xQs sv7UsPp4fbHuD5z1nnYfsFcyqlMm/0pOwAFN8ilX4vnvLjtrT4LZvaWtoEAX/3xi/kRH 6Ec+o6eYVPbl9hVieu2nELAXszhYP7LXfOkieNqioSIFNYcMy37evLJg79v4tx/owUeT D0t2kxtnA0qoL7EqRvtZKty5BgMYJQ9Jqz3b1FHWYaT1s3KIzDabvJMlFQk5DsoZUMHj rtm8U124P8Dk/7SjsBFKAZrmKh1ScbZXXeYVcUST38SqEGHbc6v+aloE0CFew00lT/II yUvQ== X-Gm-Message-State: AOJu0YwatFKQrohvzC4KoeJWFKnviqdXYqE0xLd2B98F1R2meACJrVBN flvCkOcPUWuI3YI3tzpABrjR9DOXpOLnxd87I6dHNqVNTD99IDRxsdQR2xoM X-Gm-Gg: ASbGncsVLVQvMmVG+fGMDjgNeqSUy1pTtnWuXPykknERX8vVOjdsEs3KBw+hsWZfqc2 XOiLeiP1tFCLx5iA/eYUzKBXrFOOg3dHshCsYQgZJPLCte2KU7EgxoA+GSlgmESi9hdv4RkiPDK O8+V5HUre5SEE4RsKhZRluigi/Nlfo5gwK59/6FZEYyn0A8nE7mAyyCZbk0AEfYP7gDemXHV6d5 0bfr1rigaqr67oX/SiETHfsERodWZgj87O7sWURNelNWEIlnOAqZPP7Ft0eqOhli7aBatappM7d nyv1NdE25+4TkG1wvSTPGUbCjLf3SCXOwQ== X-Google-Smtp-Source: AGHT+IGysUZewI0gsHO9Lasc6vFoku+64X/2pfmMr/ehtTXjON96XSQuCJ687t+yMldKLi+m1UdARA== X-Received: by 2002:a05:6808:190f:b0:3f4:fc5:d2b4 with SMTP id 5614622812f47-3f5584fd93emr867980b6e.2.1740702568999; Thu, 27 Feb 2025 16:29:28 -0800 (PST) Received: from denethor.localdomain ([136.50.74.45]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-5feaab9c8f9sm456509eaf.19.2025.02.27.16.29.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Feb 2025 16:29:28 -0800 (PST) From: Justin Tobler To: git@vger.kernel.org Cc: ps@pks.im, karthik.188@gmail.com, phillip.wood123@gmail.com, Justin Tobler , Jeff King Subject: [PATCH v4 3/4] builtin: introduce diff-pairs command Date: Thu, 27 Feb 2025 18:26:03 -0600 Message-ID: <20250228002604.3859939-4-jltobler@gmail.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250228002604.3859939-1-jltobler@gmail.com> References: <20250225233925.1345086-1-jltobler@gmail.com> <20250228002604.3859939-1-jltobler@gmail.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Through git-diff(1), a single diff can be generated from a pair of blob revisions directly. Unfortunately, there is not a mechanism to compute batches of specific file pair diffs in a single process. Such a feature is particularly useful on the server-side where diffing between a large set of changes is not feasible all at once due to timeout concerns. To facilitate this, introduce git-diff-pairs(1) which acts as a backend passing its NUL-terminated raw diff format input from stdin through diff machinery to produce various forms of output such as patch or raw. The raw format was originally designed as an interchange format and represents the contents of the diff_queued_diff list making it possible to break the diff pipeline into separate stages. For example, git-diff-tree(1) can be used as a frontend to compute file pairs to queue and feed its raw output to git-diff-pairs(1) to compute patches. With this, batches of diffs can be progressively generated without having to recompute renames or retrieve object context. Something like the following: git diff-tree -r -z -M $old $new | git diff-pairs -p -z should generate the same output as `git diff-tree -p -M`. Furthermore, each line of raw diff formatted input can also be individually fed to a separate git-diff-pairs(1) process and still produce the same output. Based-on-patch-by: Jeff King Signed-off-by: Justin Tobler --- .gitignore | 1 + Documentation/git-diff-pairs.adoc | 56 +++++++++ Documentation/meson.build | 1 + Makefile | 1 + builtin.h | 1 + builtin/diff-pairs.c | 195 ++++++++++++++++++++++++++++++ command-list.txt | 1 + git.c | 1 + meson.build | 1 + t/meson.build | 1 + t/t4070-diff-pairs.sh | 81 +++++++++++++ 11 files changed, 340 insertions(+) create mode 100644 Documentation/git-diff-pairs.adoc create mode 100644 builtin/diff-pairs.c create mode 100755 t/t4070-diff-pairs.sh diff --git a/.gitignore b/.gitignore index 08a66ca508..04c444404e 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ /git-diff /git-diff-files /git-diff-index +/git-diff-pairs /git-diff-tree /git-difftool /git-difftool--helper diff --git a/Documentation/git-diff-pairs.adoc b/Documentation/git-diff-pairs.adoc new file mode 100644 index 0000000000..e31f2e2fbb --- /dev/null +++ b/Documentation/git-diff-pairs.adoc @@ -0,0 +1,56 @@ +git-diff-pairs(1) +================= + +NAME +---- +git-diff-pairs - Compare the content and mode of provided blob pairs + +SYNOPSIS +-------- +[synopsis] +git diff-pairs -z [] + +DESCRIPTION +----------- +Show changes for file pairs provided on stdin. Input for this command must be +in the NUL-terminated raw output format as generated by commands such as `git +diff-tree -z -r --raw`. By default, the outputted diffs are computed and shown +in the patch format when stdin closes. + +Usage of this command enables the traditional diff pipeline to be broken up +into separate stages where `diff-pairs` acts as the output phase. Other +commands, such as `diff-tree`, may serve as a frontend to compute the raw +diff format used as input. + +Instead of computing diffs via `git diff-tree -p -M` in one step, `diff-tree` +can compute the file pairs and rename information without the blob diffs. This +output can be fed to `diff-pairs` to generate the underlying blob diffs as done +in the following example: + +----------------------------- +git diff-tree -z -r -M $a $b | +git diff-pairs -z +----------------------------- + +Computing the tree diff upfront with rename information allows patch output +from `diff-pairs` to be progressively computed over the course of potentially +multiple invocations. + +Pathspecs are not currently supported by `diff-pairs`. Pathspec limiting should +be performed by the upstream command generating the raw diffs used as input. + +Tree objects are not currently supported as input and are rejected. + +Abbreviated object IDs in the `diff-pairs` input are not supported. Outputted +object IDs can be abbreviated using the `--abbrev` option. + +OPTIONS +------- + +include::diff-options.adoc[] + +include::diff-generate-patch.adoc[] + +GIT +--- +Part of the linkgit:git[1] suite diff --git a/Documentation/meson.build b/Documentation/meson.build index 1129ce4c85..ce990e9fe5 100644 --- a/Documentation/meson.build +++ b/Documentation/meson.build @@ -42,6 +42,7 @@ manpages = { 'git-diagnose.adoc' : 1, 'git-diff-files.adoc' : 1, 'git-diff-index.adoc' : 1, + 'git-diff-pairs.adoc' : 1, 'git-difftool.adoc' : 1, 'git-diff-tree.adoc' : 1, 'git-diff.adoc' : 1, diff --git a/Makefile b/Makefile index bcf5ed3f85..56df7aed3f 100644 --- a/Makefile +++ b/Makefile @@ -1242,6 +1242,7 @@ BUILTIN_OBJS += builtin/describe.o BUILTIN_OBJS += builtin/diagnose.o BUILTIN_OBJS += builtin/diff-files.o BUILTIN_OBJS += builtin/diff-index.o +BUILTIN_OBJS += builtin/diff-pairs.o BUILTIN_OBJS += builtin/diff-tree.o BUILTIN_OBJS += builtin/diff.o BUILTIN_OBJS += builtin/difftool.o diff --git a/builtin.h b/builtin.h index 89928ccf92..e6aad3a6a1 100644 --- a/builtin.h +++ b/builtin.h @@ -153,6 +153,7 @@ int cmd_diagnose(int argc, const char **argv, const char *prefix, struct reposit int cmd_diff_files(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_diff_index(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_diff(int argc, const char **argv, const char *prefix, struct repository *repo); +int cmd_diff_pairs(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_diff_tree(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_difftool(int argc, const char **argv, const char *prefix, struct repository *repo); int cmd_env__helper(int argc, const char **argv, const char *prefix, struct repository *repo); diff --git a/builtin/diff-pairs.c b/builtin/diff-pairs.c new file mode 100644 index 0000000000..5a993b7c9d --- /dev/null +++ b/builtin/diff-pairs.c @@ -0,0 +1,195 @@ +#include "builtin.h" +#include "config.h" +#include "diff.h" +#include "diffcore.h" +#include "gettext.h" +#include "hash.h" +#include "hex.h" +#include "object.h" +#include "parse-options.h" +#include "revision.h" +#include "strbuf.h" + +static unsigned parse_mode_or_die(const char *mode, const char **end) +{ + uint16_t ret; + + *end = parse_mode(mode, &ret); + if (!*end) + die(_("unable to parse mode: %s"), mode); + return ret; +} + +static void parse_oid_or_die(const char *hex, struct object_id *oid, + const char **end, const struct git_hash_algo *algop) +{ + if (parse_oid_hex_algop(hex, oid, end, algop) || *(*end)++ != ' ') + die(_("unable to parse object id: %s"), hex); +} + +int cmd_diff_pairs(int argc, const char **argv, const char *prefix, + struct repository *repo) +{ + struct strbuf path_dst = STRBUF_INIT; + struct strbuf path = STRBUF_INIT; + struct strbuf meta = STRBUF_INIT; + struct option *parseopts; + struct rev_info revs; + int line_term = '\0'; + int ret; + + const char * const usagestr[] = { + N_("git diff-pairs -z []"), + NULL + }; + struct option options[] = { + OPT_END() + }; + + repo_init_revisions(repo, &revs, prefix); + + /* + * Diff options are usually parsed implicitly as part of + * setup_revisions(). Explicitly handle parsing to ensure options are + * printed in the usage message. + */ + parseopts = add_diff_options(options, &revs.diffopt); + show_usage_with_options_if_asked(argc, argv, usagestr, parseopts); + + repo_config(repo, git_diff_basic_config, NULL); + revs.disable_stdin = 1; + revs.abbrev = 0; + revs.diff = 1; + + argc = parse_options(argc, argv, prefix, parseopts, usagestr, + PARSE_OPT_KEEP_UNKNOWN_OPT | + PARSE_OPT_KEEP_DASHDASH | + PARSE_OPT_KEEP_ARGV0); + + if (setup_revisions(argc, argv, &revs, NULL) > 1) + usagef(_("unrecognized argument: %s"), argv[0]); + + /* + * With the -z option, both command input and raw output are + * NUL-delimited (this mode does not affect patch output). At present + * only NUL-delimited raw diff formatted input is supported. + */ + if (revs.diffopt.line_termination) + usage(_("working without -z is not supported")); + + if (revs.prune_data.nr) + usage(_("pathspec arguments not supported")); + + if (revs.pending.nr || revs.max_count != -1 || + revs.min_age != (timestamp_t)-1 || + revs.max_age != (timestamp_t)-1) + usage(_("revision arguments not allowed")); + + if (!revs.diffopt.output_format) + revs.diffopt.output_format = DIFF_FORMAT_PATCH; + + /* + * If rename detection is not requested, use rename information from the + * raw diff formatted input. Setting skip_resolving_statuses ensures + * diffcore_std() does not mess with rename information already present + * in queued filepairs. + */ + if (!revs.diffopt.detect_rename) + revs.diffopt.skip_resolving_statuses = 1; + + while (1) { + struct object_id oid_a, oid_b; + struct diff_filepair *pair; + unsigned mode_a, mode_b; + const char *p; + char status; + + if (strbuf_getwholeline(&meta, stdin, line_term) == EOF) + break; + + p = meta.buf; + if (*p != ':') + die(_("invalid raw diff input")); + p++; + + mode_a = parse_mode_or_die(p, &p); + mode_b = parse_mode_or_die(p, &p); + + if (S_ISDIR(mode_a) || S_ISDIR(mode_b)) + die(_("tree objects not supported")); + + parse_oid_or_die(p, &oid_a, &p, repo->hash_algo); + parse_oid_or_die(p, &oid_b, &p, repo->hash_algo); + + status = *p++; + + if (strbuf_getwholeline(&path, stdin, line_term) == EOF) + die(_("got EOF while reading path")); + + switch (status) { + case DIFF_STATUS_ADDED: + pair = diff_queue_addremove(&diff_queued_diff, + &revs.diffopt, '+', mode_b, + &oid_b, 1, path.buf, 0); + if (pair) + pair->status = status; + break; + + case DIFF_STATUS_DELETED: + pair = diff_queue_addremove(&diff_queued_diff, + &revs.diffopt, '-', mode_a, + &oid_a, 1, path.buf, 0); + if (pair) + pair->status = status; + break; + + case DIFF_STATUS_TYPE_CHANGED: + case DIFF_STATUS_MODIFIED: + pair = diff_queue_change(&diff_queued_diff, &revs.diffopt, + mode_a, mode_b, &oid_a, &oid_b, + 1, 1, path.buf, 0, 0); + if (pair) + pair->status = status; + break; + + case DIFF_STATUS_RENAMED: + case DIFF_STATUS_COPIED: { + struct diff_filespec *a, *b; + unsigned int score; + + if (strbuf_getwholeline(&path_dst, stdin, line_term) == EOF) + die(_("got EOF while reading destination path")); + + a = alloc_filespec(path.buf); + b = alloc_filespec(path_dst.buf); + fill_filespec(a, &oid_a, 1, mode_a); + fill_filespec(b, &oid_b, 1, mode_b); + + pair = diff_queue(&diff_queued_diff, a, b); + + if (strtoul_ui(p, 10, &score)) + die(_("unable to parse rename/copy score: %s"), p); + + pair->score = score * MAX_SCORE / 100; + pair->status = status; + pair->renamed_pair = 1; + } + break; + + default: + die(_("unknown diff status: %c"), status); + } + } + + diffcore_std(&revs.diffopt); + diff_flush(&revs.diffopt); + ret = diff_result_code(&revs); + + strbuf_release(&path_dst); + strbuf_release(&path); + strbuf_release(&meta); + release_revisions(&revs); + FREE_AND_NULL(parseopts); + + return ret; +} diff --git a/command-list.txt b/command-list.txt index c537114b46..b7ade3ab9f 100644 --- a/command-list.txt +++ b/command-list.txt @@ -96,6 +96,7 @@ git-diagnose ancillaryinterrogators git-diff mainporcelain info git-diff-files plumbinginterrogators git-diff-index plumbinginterrogators +git-diff-pairs plumbinginterrogators git-diff-tree plumbinginterrogators git-difftool ancillaryinterrogators complete git-fast-export ancillarymanipulators diff --git a/git.c b/git.c index 450d6aaa86..77c4359522 100644 --- a/git.c +++ b/git.c @@ -541,6 +541,7 @@ static struct cmd_struct commands[] = { { "diff", cmd_diff, NO_PARSEOPT }, { "diff-files", cmd_diff_files, RUN_SETUP | NEED_WORK_TREE | NO_PARSEOPT }, { "diff-index", cmd_diff_index, RUN_SETUP | NO_PARSEOPT }, + { "diff-pairs", cmd_diff_pairs, RUN_SETUP | NO_PARSEOPT }, { "diff-tree", cmd_diff_tree, RUN_SETUP | NO_PARSEOPT }, { "difftool", cmd_difftool, RUN_SETUP_GENTLY }, { "fast-export", cmd_fast_export, RUN_SETUP }, diff --git a/meson.build b/meson.build index bf95576f83..9e8b365d2a 100644 --- a/meson.build +++ b/meson.build @@ -540,6 +540,7 @@ builtin_sources = [ 'builtin/diagnose.c', 'builtin/diff-files.c', 'builtin/diff-index.c', + 'builtin/diff-pairs.c', 'builtin/diff-tree.c', 'builtin/diff.c', 'builtin/difftool.c', diff --git a/t/meson.build b/t/meson.build index 780939d49f..09c7bc2fad 100644 --- a/t/meson.build +++ b/t/meson.build @@ -500,6 +500,7 @@ integration_tests = [ 't4067-diff-partial-clone.sh', 't4068-diff-symmetric-merge-base.sh', 't4069-remerge-diff.sh', + 't4070-diff-pairs.sh', 't4100-apply-stat.sh', 't4101-apply-nonl.sh', 't4102-apply-rename.sh', diff --git a/t/t4070-diff-pairs.sh b/t/t4070-diff-pairs.sh new file mode 100755 index 0000000000..8f17e55c7d --- /dev/null +++ b/t/t4070-diff-pairs.sh @@ -0,0 +1,81 @@ +#!/bin/sh + +test_description='basic diff-pairs tests' +. ./test-lib.sh + +# This creates a diff with added, modified, deleted, renamed, copied, and +# typechange entries. This includes a submodule to test submodule diff support. +test_expect_success 'setup' ' + test_config_global protocol.file.allow always && + test_create_repo sub && + test_commit -C sub initial && + + test_create_repo main && + cd main && + echo to-be-gone >deleted && + echo original >modified && + echo now-a-file >symlink && + test_seq 200 >two-hundred && + test_seq 201 500 >five-hundred && + git add . && + test_tick && + git commit -m base && + git tag base && + + git submodule add ../sub && + echo now-here >added && + echo new >modified && + rm deleted && + mkdir subdir && + echo content >subdir/file && + mv two-hundred renamed && + test_seq 201 500 | sed s/300/modified/ >copied && + rm symlink && + git add -A . && + test_ln_s_add dest symlink && + test_tick && + git commit -m new && + git tag new +' + +test_expect_success 'diff-pairs recreates --raw' ' + git diff-tree -r -M -C -C -z base new >expect && + git diff-pairs --raw -z >actual expect && + git diff-tree -r -M -C -C -z base new | + git diff-pairs -p -z >actual && + test_cmp expect actual +' + +test_expect_success 'diff-pairs does not support normal raw diff input' ' + git diff-tree -r base new | + test_must_fail git diff-pairs >out 2>err && + + echo "usage: working without -z is not supported" >expect && + test_must_be_empty out && + test_cmp expect err +' + +test_expect_success 'diff-pairs does not support tree objects as input' ' + git diff-tree -z base new | + test_must_fail git diff-pairs -z >out 2>err && + + echo "fatal: tree objects not supported" >expect && + test_must_be_empty out && + test_cmp expect err +' + +test_expect_success 'diff-pairs does not support pathspec arguments' ' + git diff-tree -r -z base new | + test_must_fail git diff-pairs -z -- new >out 2>err && + + echo "usage: pathspec arguments not supported" >expect && + test_must_be_empty out && + test_cmp expect err +' + +test_done From patchwork Fri Feb 28 00:26:04 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Tobler X-Patchwork-Id: 13995378 Received: from mail-oi1-f169.google.com (mail-oi1-f169.google.com [209.85.167.169]) (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 0CC581C28E for ; Fri, 28 Feb 2025 00:29:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.167.169 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740702574; cv=none; b=VyIVIBj2xQ7F3qJsg85SEG2Zowq0TLoXlvDqgbdpMVeywmvCkbqFj+DFzzONzOTXBNoIjt/9p1RJhF7bXFH7mzbmV0Cek/3qivioGgWhItPMAkK5s3oJqQWqa3D8SevRTCerMtv5HfQ2ecUZTO7Cq5tSer/nQ6KV9OBbxG0VXRc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740702574; c=relaxed/simple; bh=KEZV1icyrmHYj4EV1oi0M7cx1QcoY59oT7aPcmcCbuo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=osK5Wk8FM5i7gPVqvl6EhvnOA05kqOPTJ0cX2BCMAI4IMg0qMVfNmfSJg4FoMW3TXhKrWWcnggIXqGutkJNrJfkjfIpu50d39mrnu9Jv9Pf9UhSAvJnnsQ9U7tU/pT8d+YSzx5IK65kNBA2dc5AGPqyMGbG2aqoFyxNVFmrqxIE= 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=KDVsTnSE; arc=none smtp.client-ip=209.85.167.169 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="KDVsTnSE" Received: by mail-oi1-f169.google.com with SMTP id 5614622812f47-3f40ad1574fso1110728b6e.0 for ; Thu, 27 Feb 2025 16:29:31 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1740702570; x=1741307370; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Lto5EhTwM8PkUAcfRGno8jETCVoQ4AsaBwSJVDV5jKE=; b=KDVsTnSEruniyN/Rp12NfXyEuqlKoglnTtI0L7cuMzZglwyaIlujifolf5DbSa7tJR qq9tu6s8Ya6YhMN46DIghcpwE6Bxe+RXuAwDB4RMkJzRuNzCFHTVjMI+GaI57yWOZIgP khDKCymi1pEOYAF+wU8n84C3qos2+ZSspzHRDp8U1n3Iya0XcEQjgcVwYa2cGm5WD7XZ wUkepdaYqlbePNZ3RswOmO1m/Nz57ewWlNFRUl4k8olFXizP6xjSROaQxW1K+qZqRopd OhYIummbFUzv2wP/EpFEg8mVjt8WIO5QLBDXyaXAKwYuJb3nJ1lRjvak9P4jShgJL/82 qVTA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740702570; x=1741307370; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Lto5EhTwM8PkUAcfRGno8jETCVoQ4AsaBwSJVDV5jKE=; b=OiTBCN5eIrItSMBTBGSlrvm6Wa8WofrfIzQXN+bupwIGY+UgfWB22orIJynrgBFmlO AkOHL3bzK8L2llloKLvX7p7h0B9Lb0B3ZPss16vVFZThkxaTAary/IK1ob9ONC0Xl9dp dHnjPb32oTIpXYCj1mERbcsmfda1DGKxGMWgAVijG4RmbmVdkwP3K1H+NtVcd8H3SX9N Uezu5SbdM5NWj2fLzDXMCsEjeIf7ussgHXEurjfPZg5Kpm2qUcoHWThSRHpzncOpnAy1 g+glrMcSMGcJgd//XjERlMRG43sV3FXjVutbjsa2OjFOnO//UE+19egyuc9IQzGnwfkL oBWw== X-Gm-Message-State: AOJu0Yy+T+MvqjhMDDtnb3mWFRuiikVZi/KZt1Wk2SnYQyO/JzRsmDTg 9w3p1CTG8gxWuU0gQUh3ny+WPyWJoyX1koeIrx/g68NQ/mEi4F5SUou6+X7t X-Gm-Gg: ASbGnctzkHRTl6yaSJWx+4flRGQpJUljo2aJX55s/EKIHpssZoufpLIhl3QG04j9dCz 7UiWWkp/wIEpUNhTQ0u8ofNLd5nhM4ghGgSTki4O3N1N84Y0lXRM4VtF+DN1OuX8mRL0g/Vr1vg Kmxd1hYlC2/Q2mcF/rPX2S85q6KmWZGugyGHBOEKWepHlT1RicgcxlUl7jgAA7HNj3+D3LvbZ9t p9rYIR4ktIEWBk2xnZM1YAWFmUDts4CexGH22DU6wBQJqNRLLgn0RaBQHxtir/d03ijZKVvRgr1 IrxPXEF5A88d+lWOp3e1lqwMqmMOMNb2RA== X-Google-Smtp-Source: AGHT+IH9bnXG/ad6+tZ1akIwVb9EXpl2vsRRlBF23BiJkQGRRpLnBOmtzU2icNggnyBqRcBv3qfeGQ== X-Received: by 2002:a05:6808:640f:b0:3f3:dd2f:6a9b with SMTP id 5614622812f47-3f5585f4d02mr775122b6e.30.1740702570382; Thu, 27 Feb 2025 16:29:30 -0800 (PST) Received: from denethor.localdomain ([136.50.74.45]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-5feaab9c8f9sm456509eaf.19.2025.02.27.16.29.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 27 Feb 2025 16:29:29 -0800 (PST) From: Justin Tobler To: git@vger.kernel.org Cc: ps@pks.im, karthik.188@gmail.com, phillip.wood123@gmail.com, Justin Tobler Subject: [PATCH v4 4/4] builtin/diff-pairs: allow explicit diff queue flush Date: Thu, 27 Feb 2025 18:26:04 -0600 Message-ID: <20250228002604.3859939-5-jltobler@gmail.com> X-Mailer: git-send-email 2.48.1 In-Reply-To: <20250228002604.3859939-1-jltobler@gmail.com> References: <20250225233925.1345086-1-jltobler@gmail.com> <20250228002604.3859939-1-jltobler@gmail.com> Precedence: bulk X-Mailing-List: git@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 The diffs queued from git-diff-pairs(1) are flushed when stdin is closed. To enable greater flexibility, allow control over when the diff queue is flushed by writing a single NUL byte on stdin between input file pairs. Diff output between flushes is separated by a single NUL byte. Signed-off-by: Justin Tobler --- Documentation/git-diff-pairs.adoc | 4 ++++ builtin/diff-pairs.c | 14 ++++++++++++++ t/t4070-diff-pairs.sh | 9 +++++++++ 3 files changed, 27 insertions(+) diff --git a/Documentation/git-diff-pairs.adoc b/Documentation/git-diff-pairs.adoc index e31f2e2fbb..f99fcd1ead 100644 --- a/Documentation/git-diff-pairs.adoc +++ b/Documentation/git-diff-pairs.adoc @@ -17,6 +17,10 @@ in the NUL-terminated raw output format as generated by commands such as `git diff-tree -z -r --raw`. By default, the outputted diffs are computed and shown in the patch format when stdin closes. +A single NUL byte may be written to stdin between raw input lines to compute +file pair diffs up to that point instead of waiting for stdin to close. A NUL +byte is also written to the output to delimit between these batches of diffs. + Usage of this command enables the traditional diff pipeline to be broken up into separate stages where `diff-pairs` acts as the output phase. Other commands, such as `diff-tree`, may serve as a frontend to compute the raw diff --git a/builtin/diff-pairs.c b/builtin/diff-pairs.c index 5a993b7c9d..2939d4af1d 100644 --- a/builtin/diff-pairs.c +++ b/builtin/diff-pairs.c @@ -57,6 +57,7 @@ int cmd_diff_pairs(int argc, const char **argv, const char *prefix, show_usage_with_options_if_asked(argc, argv, usagestr, parseopts); repo_config(repo, git_diff_basic_config, NULL); + revs.diffopt.no_free = 1; revs.disable_stdin = 1; revs.abbrev = 0; revs.diff = 1; @@ -108,6 +109,18 @@ int cmd_diff_pairs(int argc, const char **argv, const char *prefix, break; p = meta.buf; + if (!*p) { + diffcore_std(&revs.diffopt); + diff_flush(&revs.diffopt); + /* + * When the diff queue is explicitly flushed, append a + * NUL byte to separate batches of diffs. + */ + fputc('\0', revs.diffopt.file); + fflush(revs.diffopt.file); + continue; + } + if (*p != ':') die(_("invalid raw diff input")); p++; @@ -181,6 +194,7 @@ int cmd_diff_pairs(int argc, const char **argv, const char *prefix, } } + revs.diffopt.no_free = 0; diffcore_std(&revs.diffopt); diff_flush(&revs.diffopt); ret = diff_result_code(&revs); diff --git a/t/t4070-diff-pairs.sh b/t/t4070-diff-pairs.sh index 8f17e55c7d..c5e9972b2d 100755 --- a/t/t4070-diff-pairs.sh +++ b/t/t4070-diff-pairs.sh @@ -78,4 +78,13 @@ test_expect_success 'diff-pairs does not support pathspec arguments' ' test_cmp expect err ' +test_expect_success 'diff-pairs explicit queue flush' ' + git diff-tree -r -M -C -C -z base new >expect && + printf "\0" >>expect && + git diff-tree -r -M -C -C -z base new >>expect && + + git diff-pairs --raw -z actual && + test_cmp expect actual +' + test_done