git: a8347f442824 - main - git-arc: Add a diff subcommand

From: Devin Teske <dteske_at_FreeBSD.org>
Date: Thu, 13 Aug 2026 20:33:34 UTC
The branch main has been updated by dteske:

URL: https://cgit.FreeBSD.org/src/commit/?id=a8347f44282408fb1a4c0aa611ecdf469bc030a8

commit a8347f44282408fb1a4c0aa611ecdf469bc030a8
Author:     Devin Teske <dteske@FreeBSD.org>
AuthorDate: 2026-08-13 20:31:36 +0000
Commit:     Devin Teske <dteske@FreeBSD.org>
CommitDate: 2026-08-13 20:31:36 +0000

    git-arc: Add a diff subcommand
    
    Show the differences between local commits and their associated
    Phabricator reviews, i.e., what "git arc update" would upload.  For
    each commit, the review's current raw diff is applied to the commit's
    parent in a temporary index and the resulting tree is compared against
    the commit itself.  An empty diff means the commit and the review are
    in sync.
    
    This makes it easy to check whether local amendments have diverged
    from the posted review before updating it, or to confirm that a
    review is current before landing.
    
    Reviewed by:    markj
    Differential Revision:  https://reviews.freebsd.org/D58789
---
 tools/tools/git/git-arc.1  | 23 ++++++++++++++++++++++-
 tools/tools/git/git-arc.sh | 45 +++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/tools/tools/git/git-arc.1 b/tools/tools/git/git-arc.1
index da5732fdba26..655f81c971c5 100644
--- a/tools/tools/git/git-arc.1
+++ b/tools/tools/git/git-arc.1
@@ -24,7 +24,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd April 27, 2026
+.Dd August 11, 2026
 .Dt GIT-ARC 1
 .Os
 .Sh NAME
@@ -39,6 +39,8 @@
 .Op Fl p Ar parent
 .Ar commit-ref Op Ar commit-ref ...
 .Nm
+.Cm diff Ar commit-ref Op Ar commit-ref ...
+.Nm
 .Cm list Ar commit-ref Op Ar commit-ref ...
 .Nm
 .Cm patch
@@ -117,6 +119,19 @@ Specify the parent of the first commit in the list.
 This is useful when adding more commits on top of an already existing
 stack in Phabricator.
 .El
+.It Cm diff
+Show the differences between the specified commits and their associated
+Differential Revisions, that is, what
+.Cm update
+would upload.
+For each commit, the current raw diff of the associated revision is
+applied to the commit's parent, and the result is compared against the
+commit itself.
+An empty diff means the commit and the revision are in sync.
+.Pp
+Since the revision's diff is applied to the local commit's parent, the
+comparison may fail or show unrelated changes if the commit has been
+rebased since the revision was last updated.
 .It Cm list
 Print the associated Differential Revisions for the specified commits.
 .It Cm patch
@@ -271,6 +286,12 @@ The title of the commit must be the same as it was when the review
 was created.
 Note that the review description is not automatically updated.
 .Pp
+Show what an update of the review corresponding to HEAD would upload,
+e.g., to check whether local amendments have diverged from the review:
+.Bd -literal -offset indent
+$ git arc diff HEAD
+.Ed
+.Pp
 Apply the patch in review D12345 to the currently checked-out tree,
 and stage it:
 .Bd -literal -offset indent
diff --git a/tools/tools/git/git-arc.sh b/tools/tools/git/git-arc.sh
index d8551630f7e2..c34d47160749 100755
--- a/tools/tools/git/git-arc.sh
+++ b/tools/tools/git/git-arc.sh
@@ -4,6 +4,7 @@
 #
 # Copyright (c) 2019-2021 Mark Johnston <markj@FreeBSD.org>
 # Copyright (c) 2021 John Baldwin <jhb@FreeBSD.org>
+# Copyright (c) 2026 Devin Teske <dteske@FreeBSD.org>
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions are
@@ -58,6 +59,7 @@ Usage: git arc [-vy] <command> <arguments>
 
 Commands:
   create [-dl] [-r <reviewer1>[,<reviewer2>...]] [-s subscriber[,...]] [<commit>|<commit range>]
+  diff <commit>|<commit range>
   list <commit>|<commit range>
   patch [-bcrs] <diff1> [<diff2> ...]
   stage [-b branch] [<commit>|<commit range>]
@@ -429,6 +431,41 @@ gitarc__create()
     done
 }
 
+#
+# Show the differences between local commits and their associated
+# Phabricator reviews, i.e., what "git arc update" would upload.  The
+# review's tree is reconstructed by applying its current raw diff to the
+# local commit's parent in a temporary index, and is then compared
+# against the local commit.
+#
+gitarc__diff()
+{
+    local commit commits diff rawdiff rtree
+
+    commits=$(build_commit_list "$@")
+
+    for commit in $commits; do
+        diff=$(commit2diff "$commit")
+
+        rawdiff=$(xmktemp)
+        fetch -q -o "$rawdiff" "https://reviews.freebsd.org/$diff.diff" ||
+            err "could not fetch ${diff}.diff"
+
+        rtree=$(
+            export GIT_INDEX_FILE="$(xmktemp)"
+            git read-tree --quiet "$commit~" &&
+                git -C "$(git rev-parse --show-toplevel)" apply \
+                    --cached "$rawdiff" &&
+                git write-tree
+        ) || err "cannot apply $diff to $commit~ (rebased since last update?)"
+
+	echo "Comparing $diff against" \
+		"$( git rev-parse --short "$commit" )..." >&2
+
+        git diff "$rtree" "$commit"
+    done
+}
+
 gitarc__list()
 {
     local chash commit commits diff openrevs title
@@ -837,7 +874,7 @@ else
 fi
 
 case "$1" in
-create|list|patch|stage|update)
+create|diff|list|patch|stage|update)
     ;;
 *)
     err_usage
@@ -869,10 +906,10 @@ if [ -n "$GIT_PAGER" ]; then
     PAGER=$GIT_PAGER
 fi
 
-# Bail if the working tree is unclean, except for "list" and "patch"
-# operations.
+# Bail if the working tree is unclean, except for "diff", "list" and
+# "patch" operations.
 case $verb in
-list|patch)
+diff|list|patch)
     ;;
 *)
     require_clean_work_tree "$verb"