From patchwork Fri Feb 28 21:33:43 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Tobler X-Patchwork-Id: 13997167 Received: from mail-ot1-f42.google.com (mail-ot1-f42.google.com [209.85.210.42]) (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 7C47F1D14FF for ; Fri, 28 Feb 2025 21:37:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.42 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740778649; cv=none; b=OiAXVrG+jjyweOcsnem+0spUbyvuI9CuGqJThuagnb5gkw0CkRbjdlZe3Bc28BgXhAHuLi9srLwkfOULJMUOrsbBpGIfjHMWbGKwj294c/jHE+SMUl2ZCgamOF3lbRAK7EYWV7bfPHTlOKZEajyaKVx4ctwXUhwIOTwws/IK/Po= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740778649; c=relaxed/simple; bh=HYZ5VGtA4GCCDe7sBh9xrA92vGqyzGbqPcOQ4GDLiD8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=t2iTn63XR/KBISdvrl85cyLmISndkm2hlWeiGikOxsr1dZ3bF8jbp9ctsdLE5Qj4u8R955TkfiPuodpYwuwI8GDKlKnuiWgJNixB+4SPZL4iLYvxy0YLnzXyBnUZ9pFi/nmcTp0Y9x2JaVrAegZE1sXpmplWlGYVSHic+F9XZcM= 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=dQpAMtlp; arc=none smtp.client-ip=209.85.210.42 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="dQpAMtlp" Received: by mail-ot1-f42.google.com with SMTP id 46e09a7af769-7272a839f3dso681374a34.3 for ; Fri, 28 Feb 2025 13:37:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1740778646; x=1741383446; 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=ckOzq8PgJzuzv0amvxCPQqI+rojvDGfMOfTmgTr0nfw=; b=dQpAMtlpGz1FXH1XQSKyJ2H7quHRNMhcJSsIjgjG6GTCSlCyvKMjM7PBzHxwCH4ylK k8d4611glASCca8Or8xL8ftwHXsbWtDraxlo49zpMtSXYwTkReNFhiSttebs7RIn/U3e gP1r8vJLVhY7sDb2jH1lyOoNPkiuzmbGvgs+XI+fKOI8ufzSXbUUIu4Y93LFeIPMVoyM nh1KjlgEN1EXl0tGO17Pa1xk+suKowrVD8Y1AaZrCf2QXU3fmFaLytwYIoPyd+eIUeXN K7I+ynlhaEeqSQlO9giIe7a7BvTAgd1XdPoth8oE6XBg9XEpViWw31CmZheBBzPNKKvT p0Bw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740778646; x=1741383446; 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=ckOzq8PgJzuzv0amvxCPQqI+rojvDGfMOfTmgTr0nfw=; b=av2dD/A86sEIVJOP91UhfeOMDYfErF4DfbRLPRhDjs7ah3y0WHxdoL8/B+r/VBUrwH KoutUlL9Fvfoj3QKesMx1htgYmJPTcAwECMc17uDlpyRkLDxOCNj4UDzzKBe3BI6QqMi 6M8eAK5L9/eTYWhM1x0LKtxT5PEFLB119XXxo7hB54A3aN27y/iNvXj/4QwSyJWANKa1 Fv7SHblHWLtgGfWnqs7HOQDspG1ZSysgXtQbRqgKBTwn52lBgrNHAEiesUujSKdSEG3K K5LfRgAHzs3DybCfYjouMBQkGYXCoBXjOdUIvGCQSFMBGAMYEqgf8YVOFsnBEhF4YARj 6Uqw== X-Gm-Message-State: AOJu0Yxc1MmQWZhVRzU1yjGBNl95/lElq47ue7NRfziXaolPHdYEdDyj x00gJJ4qc1cYIjM1WBroRUlIHJ5ekNEDxIi919msrJeSk/OMXkb19YWdbjjP X-Gm-Gg: ASbGncsOJCPdI6WHNjOLxo9Fny4UvUxON8iCSSiNqPQjhb5iKR1cTECNEvN9h5XDyAj NwRkFYrsIfRmjMVYXwlXshzAgMu3MSz5gZ1a9lINwtHkcIDZdZlv3aZmnJUD6m+ezmcJYhkN1Wr muJezuSB0HC3YtlltYL4piJ2wuzpMAmhkVopjjpr03+wfPlKV8mBrpyTW/aJUwH3Truf82QMx7I im7UUKzws4IMdHfZkUCz+5HpLXMtycK2Fh1Ogwlh1HixuXB8LUpWq3eUABiWWf6tjAgzVFn7Gbj gtaBKp+bkRDOB/xSNngjz1BANOfWfZP9yWjQ2975FNih30VGhA0S X-Google-Smtp-Source: AGHT+IHJ54vpQtYYAuDPWxDo13Lh8fMK6GwcO1JyNGPLeCCR3NhV3cwXnlrw7w35upz7q7Xa6MxZUA== X-Received: by 2002:a05:6830:6d13:b0:727:41d9:bcd3 with SMTP id 46e09a7af769-728b829e750mr3386477a34.13.1740778646167; Fri, 28 Feb 2025 13:37:26 -0800 (PST) Received: from denethor.localdomain ([136.50.74.45]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-5feaad46205sm783589eaf.36.2025.02.28.13.37.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2025 13:37: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 v5 1/4] diff: return diff_filepair from diff queue helpers Date: Fri, 28 Feb 2025 15:33:43 -0600 Message-ID: <20250228213346.1335224-2-jltobler@gmail.com> X-Mailer: git-send-email 2.49.0.rc0 In-Reply-To: <20250228213346.1335224-1-jltobler@gmail.com> References: <20250228002604.3859939-1-jltobler@gmail.com> <20250228213346.1335224-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 21:33:44 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Tobler X-Patchwork-Id: 13997168 Received: from mail-oo1-f45.google.com (mail-oo1-f45.google.com [209.85.161.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 A89851D9A5D for ; Fri, 28 Feb 2025 21:37:28 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740778650; cv=none; b=nWhTdcKxkuMZtxvstEcuG43rQp4Df22ObLuUs2iV3XWpQ7Ui+hSX0PupIJwUB89n1zeEVF58zr3BeIQmEW5lPygM8KrIU6b9leafrvaQr3XPVqnP1uNhueYWy3923jd4OICML2PBgpdjRCag2/LmTTiW0T+K7r2Q+g4BUzoIbOk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740778650; c=relaxed/simple; bh=W4K38PciIoZgzd0qqvYu/91hH87Q50ilHJ7WMwEHi6A=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=O0QYOe0uurx3Rp1vTWcQAXnRmZDKK+JKD4pdyapDkw6ZDoyWMKrwTemShVWpASPkgE0Uyph5+czH5MgK3IGnWM4p49lOJAnJ0upibuORj+5cOJwWr6DvbOQzIOsbdO1+qiGkOIVJykmPSjrXFyhZW7G1sW2lJy2VEeLFvkX9Shw= 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=PvlLQMFk; arc=none smtp.client-ip=209.85.161.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="PvlLQMFk" Received: by mail-oo1-f45.google.com with SMTP id 006d021491bc7-5fc13355a5bso1191549eaf.3 for ; Fri, 28 Feb 2025 13:37:28 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1740778647; x=1741383447; 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=WaSF9TM/cX12hczY7WmoU3wMkop2R4VbxstxYzLfbwo=; b=PvlLQMFkRUYw5Fp2qEV02Y4bUojm/MjGMAKi1X+yXsxic4QO42qitwjGHpkaA12aZH 96N3c1B/X9uT1XZE6ZMBPqvbseZ6eKvemzUBVv2fcoZZ0S9Dqzmp6xiOV/qkDXBkYVXT Smty7WbvsrnFLliBtE9XtiMaCkBh132TLp0UNA1JQkYVp+3fTrAJJphEF5hWjtzxT/df C/Qz7yDVgNnhz03Gz75kVzvx49fhoqhTSGyc8aHz88/COEWX64gB31ws/+TkF6B48ZHe aZycdRjSlZPn8sPlP7fD3ZlbA6gDGMBh52cW0RoioDE5klyGBd87bC0ss8G08P00g1xS m8YQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740778647; x=1741383447; 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=WaSF9TM/cX12hczY7WmoU3wMkop2R4VbxstxYzLfbwo=; b=PjJspPUimhvx1swj2dU5bbNui1/udrstzKMWqAfjvgDilfcgfChFWbPhO5gNSCSzVc d/+H/MYeE9yQ27V3PAUnTb+LXw11O89cf6goIM5q/ftzzhwuBezkiDD9X+bYoctad7XB HwRexuv0fcLR9LZ6mwBX5FXoAITAVUR0jlFHUV9fghhFL/wHtEaTcM9ndSybniCsPi+W vDBT0s8WRZBQaPXabDLMaGST9o2pX7jYr/QJHPgwGTJienKYO1aTcPl7bPytwElIV6Bj +2zRFP7YC2XdrOixLNYDEPdgTZWUo9BW4mYn931OGBl0QfnbxHkUyuMe4zGBhS4gZLiK 3ovA== X-Gm-Message-State: AOJu0YxYxwcxce//Gt37npOKMRCeCyXoI31lLowT2O5Mo0gVPKL9KQ+U JNwLaQLvYgA36/P3OKHUWck+6cim2MlymVrPrlRu38+fzqlToClu46hJ1WhD X-Gm-Gg: ASbGncv8ZjjMMyPesdBPBGx7HdeaK87Ha15EgKl5TqAt+SSzFG+ATwM/EXF+MMbQeBL LPNCboxaPKCLq42gLPReWaimzrIveW3B5G2gzeZ2HM+uwo044SypbdflsI+gbh4laqS92GxPicF IM9Yns3tlFFzUY7h6vxYA+EAYZYF7GxGU25I2eFQ+Q9kCYr5p8V7gW7Ec0qnqvWY0sGHx4aVRKh 1nmIjC6NxE39XrRJ9FqxRHLOUIlec4oH1SFB+eGXIRXe/ySUlUblNyk7TttTzypRN6Wv94k7VlZ JJyZyOFTs8km384TmMRyrns+y2EKbyU6YLOyqKzhhNv8T7ygthud X-Google-Smtp-Source: AGHT+IFI5OkTphuDdbGemCHjIN1ivktVA7KNo3t+9Bd77Vyq3iPE2j99xaVKb6Lzh3dOS3vJdz8BVw== X-Received: by 2002:a05:6820:1629:b0:5fd:896:f222 with SMTP id 006d021491bc7-5feb355e110mr2978841eaf.4.1740778647185; Fri, 28 Feb 2025 13:37:27 -0800 (PST) Received: from denethor.localdomain ([136.50.74.45]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-5feaad46205sm783589eaf.36.2025.02.28.13.37.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2025 13:37: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 v5 2/4] diff: add option to skip resolving diff statuses Date: Fri, 28 Feb 2025 15:33:44 -0600 Message-ID: <20250228213346.1335224-3-jltobler@gmail.com> X-Mailer: git-send-email 2.49.0.rc0 In-Reply-To: <20250228213346.1335224-1-jltobler@gmail.com> References: <20250228002604.3859939-1-jltobler@gmail.com> <20250228213346.1335224-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 21:33:45 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Tobler X-Patchwork-Id: 13997169 Received: from mail-oo1-f54.google.com (mail-oo1-f54.google.com [209.85.161.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 1E3E91DD0EF for ; Fri, 28 Feb 2025 21:37:29 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.161.54 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740778652; cv=none; b=fykyVImAbVhEIpg/UbG1FfPhcjQgftUImKtNE1a7C/D/sFaSuMreUaNEEPNqy9Nr3tR7aNbv6vFHIT88TsYOfAdjt4g74r7cuNr0MrWinihOr7y9I1RGgQ52CQ0YP+DCGtg4zqtF3oA/1JE7j7rKDDAARULLyCWjrj0W0+S5WsI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740778652; c=relaxed/simple; bh=4+X6zwzD5GcAj+yDwApSvN4jOtrCTfE2EoDyeoPjfjQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=ZBR3ipl33FLenxdpyUaZ3Ab0vOqSrb8HC+Qj0jTeGH9bsmFhorXpDIjCyXeemiJeynZGhQMTvSsbyBQh2LYYyCC4UrNRf3ZKNuz746GTy/O2KiaO7LzKfiozgrlDI8YCciSSm0GGtNS4QuwNkfeASzulXRSgMu3BfSxwCTA4gic= 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=Qqy/mvpE; arc=none smtp.client-ip=209.85.161.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="Qqy/mvpE" Received: by mail-oo1-f54.google.com with SMTP id 006d021491bc7-5fc13355a5bso1191565eaf.3 for ; Fri, 28 Feb 2025 13:37:29 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1740778648; x=1741383448; 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=iL8xyT79Z45q71m81W5VYqHXlIcCTVtGev5cKil9HzA=; b=Qqy/mvpECoVHKLFOpyBsB4QlbuQ7/mUIRhd/MgeS+Kl7dQIFHkafnIaCFDRomlipmo MSosjTYtVMqSm5dEP8bobMlk0O5lXbbm3mMK4x/fHLicMs27RrhhiaEI84YtVngnZCl5 rGmIznX+ERneLnQ1dtm8kF1mIHeUB6wpT0d1UtS/7StRFEVGil/JWuG5FdHRYGwPMYXV rFvqhcKZbhBeCbRwLJOP+hFeq9UrFQQiWm2gmEXCpGRCYFQe1qbfcoe3We+8qub9L1sB O1PRLVrq5J2jIb3ruFP7AYgV/6mPv/6tgSjshFt6iUKrEdLm3UH3jWn5Ep1koZQ+myba mO+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740778648; x=1741383448; 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=iL8xyT79Z45q71m81W5VYqHXlIcCTVtGev5cKil9HzA=; b=UFzaF7oQT44onBAPaBt/d9nru5Zvy13UTa3GKyp61zIuNLu7R0KHD16eeGOn9s3td8 qOV/kiTr6XmdAiwXBuAGcxyGLLsDKPT6wVAOQEAjYUPuH7+1WQi16Eg9aM5LSoqBhGpH lc+WPRYZEIE6WhF0VUVfZwXxtznMDscF1ZW5M1/QdQwhjuZPf4198cKKKyTShzzKDrmN tqDRjdq/npMYrawS5c66pO35SE0Y2LueAk1ReqRSSiTEQP2dxuhfn8Biob/olk2NJvDZ L/ajxPHo9Z7b2Qhw99V9zrLbVZBYtc1YMPN7T0Vxl53v9e1Z7733LqPLiT8bwKVY8laB Ehaw== X-Gm-Message-State: AOJu0Yz0N7j3vPqYRX830ef8UjqnUQy50cykScmC9iTOlpjoO1x2HpVm DbIK2zhtqjitmTASRrGRPceDpZj5wcJv0fRMqodOtGDsVEyqOY2PSGtwUKup X-Gm-Gg: ASbGncv4waNp8Ek+rM7oELpSohZTyu/F8sq0NPAFCGyTHn/x09B2po9rfSGoIE4fZdi tjLhDswg2r3N22iATEcQ2rvLW4B05mhUT18D2ZRg2iDDCNmsNlMX34BvHmvmQ1oVPhWZxxRwstD B6rYv3rfxFTnDUVJSUz3IRXDcWUDIRjpZgiAxUx4Sebx3IC+Y4yqSpbVTzLMGN4r7Q47DV8tt71 M6QOZJNYu49OSqbymUbqw6c/Wkm3pdySqjT2a9j4L5lMo3jeA4+KDgnU3Z04ZFRv4QnEFiSr+re e065X5ZYImWtrPzVqGFPb3TSTgfR/CY0RfbhR+q6bu7exzc9wDB7 X-Google-Smtp-Source: AGHT+IEB33cHSVRI/iuu1X+yRxJVJR9NIGk86Yeh0R7lvSbKxZSNXYI8dLy9JSP9cw9ysowtsPI92w== X-Received: by 2002:a05:6820:2908:b0:5fc:f3b8:78c2 with SMTP id 006d021491bc7-5feb355dff6mr3138680eaf.3.1740778648514; Fri, 28 Feb 2025 13:37:28 -0800 (PST) Received: from denethor.localdomain ([136.50.74.45]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-5feaad46205sm783589eaf.36.2025.02.28.13.37.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2025 13:37:27 -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 v5 3/4] builtin: introduce diff-pairs command Date: Fri, 28 Feb 2025 15:33:45 -0600 Message-ID: <20250228213346.1335224-4-jltobler@gmail.com> X-Mailer: git-send-email 2.49.0.rc0 In-Reply-To: <20250228213346.1335224-1-jltobler@gmail.com> References: <20250228002604.3859939-1-jltobler@gmail.com> <20250228213346.1335224-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 | 193 ++++++++++++++++++++++++++++++ command-list.txt | 1 + git.c | 1 + meson.build | 1 + t/meson.build | 1 + t/t4070-diff-pairs.sh | 81 +++++++++++++ 11 files changed, 338 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..6be17c1abd --- /dev/null +++ b/builtin/diff-pairs.c @@ -0,0 +1,193 @@ +#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 builtin_diff_pairs_usage[] = { + N_("git diff-pairs -z []"), + NULL + }; + struct option builtin_diff_pairs_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(builtin_diff_pairs_options, &revs.diffopt); + show_usage_with_options_if_asked(argc, argv, builtin_diff_pairs_usage, 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, builtin_diff_pairs_usage, + PARSE_OPT_KEEP_ARGV0 | PARSE_OPT_KEEP_DASHDASH); + + 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..0878ad0ad1 --- /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 && + git init sub && + test_commit -C sub initial && + + git init 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 21:33:46 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Justin Tobler X-Patchwork-Id: 13997170 Received: from mail-ot1-f45.google.com (mail-ot1-f45.google.com [209.85.210.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 E40201D14FF for ; Fri, 28 Feb 2025 21:37:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.210.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740778652; cv=none; b=omERAV560+8N7fkpbwhboFz9twpVgv+gascHYqonQo34/YnbcLHQbI3D+S5WVzi06a/OiCrzIxIo5Er89FeQInMPeWpCEvjM1gS4GupJlrUW3DEB6v/vHrQZFRiY3seO+75x5B86ksaJTb3fMM0hSdVrhyJktuteE3Fu2U5xJEo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1740778652; c=relaxed/simple; bh=CxSqSnTuRV+NRqb67Ff7ZtPRfzZcGCou79EaOQeCgFY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=W0b1qr3nAW5RPn0kInucl+3YNrLktpesHKsvjoe2PAJhNwfk3vwfF0fSOwqgOgWdOKq3LoI+B7kRDULcTNIL9sGzS9hgowZQ7rQPPAjObZVbNPJ+kB6npbJxbq9vwQ6v9svgdOc7RLD/Uo9JmNGccqBp6gsdAZhic4IlqQEIxLg= 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=TJqFSLIl; arc=none smtp.client-ip=209.85.210.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="TJqFSLIl" Received: by mail-ot1-f45.google.com with SMTP id 46e09a7af769-72720572addso1755277a34.3 for ; Fri, 28 Feb 2025 13:37:30 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1740778650; x=1741383450; 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=v9Z9eagWQLRK5m8mqmChH09cUODz8AToYNzmWMBTQvM=; b=TJqFSLIlI2z/O1wdQ1ROUsFyNV7t7u0JxYiA4N9hU1KzqdLZPATDsru6qTTbaurtYg D2Ya9kQNd3dHn15O13wHRBJc2FK8795+HDMRI00dgdy9XtAK0PuJTyosmKg+UiRJ8ZCJ TNycJjO8BXtFlSHuEZV1D0EL6PeJDRAyVmr8goETaQncR7dtTUGP6qvF1TM+ZKHHe1oP pBxMZA2YS4AwUduSk61FEi8C9jLXuwoULTJi/ck9n4JECS8sLibv0EskzJ/4WxbMpQXK 9mR6f1mkcp1J/+JHreFZiQHaHP50axRDgMkEau2My8qIsMpiXS0VYxgJQfeS1lPsNYmh Hbtg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1740778650; x=1741383450; 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=v9Z9eagWQLRK5m8mqmChH09cUODz8AToYNzmWMBTQvM=; b=l1ZDHXrV+dVkd3XNGl2M5/HlsRChZyuQtddocDZheCX1SCEw+a0z04ETYsz+B3U3g0 SRsw3AAyV1v3tNGauxEiwCQFs5GymuyGJPdazpqaox76r7kg9/Sgkx41q4clww1LtAVf DXWJHvvZU9IEcMj4t//G0uN/ymMsO7EcXVIBt3PfgcgD/cXvlilZ38ZsAumCbr0N1C5W ISHkFkPPjV6Fs9WnTmWv2PKG7T0USRwUxAUcv1F7ZLmMHK+C0ISmANq9kQu2572HSw+C H7uPePAhShx3gpuUM6cDdb/4kzfNMURZz/IfXWDkksf0C9grNxuVMF8b+deGGj5OfKe4 Ro+w== X-Gm-Message-State: AOJu0Yw+MB2FxFOIEOnVFj4/6lWeA8PlSmqvM/OsnFAwtDKZivjTQ0kz KUrMDwI4cRTtaSGP4lTCQ36bIBZXu9/9bGw4ak/lP0ld37/4WG534EIv8kBh X-Gm-Gg: ASbGncsfA3BQaO+f5fb4oeQ1xDp5LBuIJeSl079T1/3wW4Bx8uPPZmlh6DJVGGQwuxA cwtM2+AI9IENr3GSzIAjiX1ff5Fj0XlI3fbL3T8JnIjZ1YtGy/rCQhLGzYf6ouC5HrBisuA5XJn AWoVJEKDL/mUuXRdnsFSXrO6x2QREx9nDbvq4hH9HTBS7TxOR89wXzsSU0fDXmhsd580kA0zVzI YuS1PNuOeudiBzgItyDsUCKEQdEDqz6FPMB+j+UbwH+YVv9i6fqmF3VlUMbM+JgU+kX/N8+MoNg XjryQmkY4+ZAGI/FXbSXW1oyddTxK92fzKIe8ty91UALyB027ZmQ X-Google-Smtp-Source: AGHT+IGYZwEQFVckBAOEhMqeJ34F6mwbiikyNUfQ8XAEjvGDwl8IrHc9dd7YZKBqpQunoseD3/zaWA== X-Received: by 2002:a05:6830:710f:b0:71e:4fc:6ef6 with SMTP id 46e09a7af769-728b828625amr3260655a34.12.1740778649658; Fri, 28 Feb 2025 13:37:29 -0800 (PST) Received: from denethor.localdomain ([136.50.74.45]) by smtp.gmail.com with ESMTPSA id 006d021491bc7-5feaad46205sm783589eaf.36.2025.02.28.13.37.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 28 Feb 2025 13:37: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 v5 4/4] builtin/diff-pairs: allow explicit diff queue flush Date: Fri, 28 Feb 2025 15:33:46 -0600 Message-ID: <20250228213346.1335224-5-jltobler@gmail.com> X-Mailer: git-send-email 2.49.0.rc0 In-Reply-To: <20250228213346.1335224-1-jltobler@gmail.com> References: <20250228002604.3859939-1-jltobler@gmail.com> <20250228213346.1335224-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 6be17c1abd..71c045331a 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, builtin_diff_pairs_usage, parseopts); repo_config(repo, git_diff_basic_config, NULL); + revs.diffopt.no_free = 1; revs.disable_stdin = 1; revs.abbrev = 0; revs.diff = 1; @@ -106,6 +107,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++; @@ -179,6 +192,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 0878ad0ad1..70deafb860 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