git range-diff compares two commit ranges (two versions of
a branch, e.g. before and after a rebase). Its output is difficult to
comprehend, though. It’s a diff between diffs, presented in one dimension
using two columns of pluses/minuses/spaces. Wouldn’t it be better
if we used two dimensions and some nice colors?
Table of Contents
Motivation/Example
To illustrate my point, let’s take a work-in-progress1
pull request from
xmonad-contrib, git
rebase it to autosquash the fixup commits, and then use git
range-diff to see what the rebase actually did:
$ gh pr checkout 836
$ git checkout -b wx-partial-rebase
$ git rebase -i --keep-base --autosquash origin/master
$ git range-diff wx-partial...wx-partial-rebase
1:  05a5291e ! 1:  d5c45f98 X.Prelude: Add infinite stream type
    @@ XMonad/Prelude.hs: multimediaKeys = filter ((/= noSymbol) . snd) . map (id &&& s
     +  type (Item (Stream a)) = a
     +
     +  fromList :: [a] -> Stream a
    -+  fromList []       = errorWithoutStackTrace "TODO"
     +  fromList (x : xs) = x :~ fromList xs
    ++  fromList []       = errorWithoutStackTrace "XMonad.Prelude.Stream.fromList: Can't create stream out of finite list."
     +
     +  toList :: Stream a -> [a]
     +  toList (x :~ xs) = x : toList xs
2:  9dfe5594 = 2:  a11625d9 X.L.Groups: Rewrite gen using infinite streams
3:  c8a08103 = 3:  06855306 Import X.Prelude unqualified if necessary
4:  90f16434 = 4:  2de422fe Reduce head usage
5:  00a457fd < -:  -------- fixup! X.Prelude: Add infinite stream type
This is a simple example—neither the old (05a5291e) nor new (d5c45f98) commits
remove any lines, so the inner column is always +. Some of you can therefore
figure out that the difference is that the old commit adds a line with “TODO”
in it, while the new adds a line (somewhere else!) with a meaningful error
message instead.
Now let’s look at it side by side (in 2 dimensions):
$ git si-range-diff wx-partial...wx-partial-rebase
(note: si-range-diff is my alias; don’t try this at home—just yet)
────────────────────────────────────────────────────────────────────────────────────────────────────────────
1:  05a5291e ! 1:  d5c45f98 X.Prelude: Add infinite stream type
──────────────────────────────────────────────────────────────────────────────────┐
 XMonad/Prelude.hs: multimediaKeys = filter ((/= noSymbol) . snd) . map (id &&& s │
──────────────────────────────────────────────────────────────────────────────────┘
+  type (Item (Stream a)) = a                         │ +  type (Item (Stream a)) = a
+                                                     │ +
+  fromList :: [a] -> Stream a                        │ +  fromList :: [a] -> Stream a
+  fromList []       = errorWithoutStackTrace "TODO"  │ 
+  fromList (x : xs) = x :~ fromList xs               │ +  fromList (x : xs) = x :~ fromList xs
                                                      │ +  fromList []       = errorWithoutStackTrace "XMon↵
                                                      │ ad.Prelude.Stream.fromList: Can't create stream out↵
                                                      │  of finite list."
+                                                     │ +
+  toList :: Stream a -> [a]                          │ +  toList :: Stream a -> [a]
+  toList (x :~ xs) = x : toList xs                   │ +  toList (x :~ xs) = x : toList xs
────────────────────────────────────────────────────────────────────────────────────────────────────────────
2:  9dfe5594 = 2:  a11625d9 X.L.Groups: Rewrite gen using infinite streams
────────────────────────────────────────────────────────────────────────────────────────────────────────────
3:  c8a08103 = 3:  06855306 Import X.Prelude unqualified if necessary
────────────────────────────────────────────────────────────────────────────────────────────────────────────
4:  90f16434 = 4:  2de422fe Reduce head usage
────────────────────────────────────────────────────────────────────────────────────────────────────────────
5:  00a457fd < -:  -------- fixup! X.Prelude: Add infinite stream type
Much better. It’s easier to see what’s going on here. Left side is the old diff, right side is the new one. Both are syntax-highlighted using foreground (text) colours. The inter-diff diff is syntax-highlighted using background colours—the removed line on the left side is dark red, the added one on the right side is darkish green.
Here’s a longer example from the Linux kernel, which I believe requires
superhuman abilities to understand (made more difficult by the ## and @@
lines appearing in the context of neighbouring diff hunks):
Expand/Collapse
1:  81f8e8008c9a ! 1:  05fce90455a2 mm: abstract the vma_merge()/split_vma() pattern for mprotect() et al.
    @@ Commit message
         parameters, create inline wrapper functions for each of the modify
         operations, parameterised only by what is required to perform the action.
     
    +    We can also significantly simplify the logic - by returning the VMA if we
    +    split (or merged VMA if we do not) we no longer need specific handling for
    +    merge/split cases in any of the call sites.
    +
         Note that the userfaultfd_release() case works even though it does not
         split VMAs - since start is set to vma->vm_start and end is set to
         vma->vm_end, the split logic does not trigger.
    @@ Commit message
         - vma->vm_start) >> PAGE_SHIFT, and start - vma->vm_start will be 0 in this
         instance, this invocation will remain unchanged.
     
    -    Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
    +    We eliminate a VM_WARN_ON() in mprotect_fixup() as this simply asserts that
    +    vma_merge() correctly ensures that flags remain the same, something that is
    +    already checked in is_mergeable_vma() and elsewhere, and in any case is not
    +    specific to mprotect().
    +
         Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
    +    Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
     
      ## fs/userfaultfd.c ##
     @@ fs/userfaultfd.c: static int userfaultfd_release(struct inode *inode, struct file *file)
    @@ fs/userfaultfd.c: static int userfaultfd_release(struct inode *inode, struct fil
     -				 vma->vm_file, vma->vm_pgoff,
     -				 vma_policy(vma),
     -				 NULL_VM_UFFD_CTX, anon_vma_name(vma));
    -+		prev = vma_modify_flags_uffd(&vmi, prev, vma, vma->vm_start,
    -+					     vma->vm_end, new_flags,
    -+					     NULL_VM_UFFD_CTX);
    +-		if (prev) {
    +-			vma = prev;
    +-		} else {
    +-			prev = vma;
    +-		}
    ++		vma = vma_modify_flags_uffd(&vmi, prev, vma, vma->vm_start,
    ++					    vma->vm_end, new_flags,
    ++					    NULL_VM_UFFD_CTX);
    + 
    + 		vma_start_write(vma);
    + 		userfaultfd_set_vm_flags(vma, new_flags);
    + 		vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
     +
    - 		if (prev) {
    - 			vma = prev;
    - 		} else {
    ++		prev = vma;
    + 	}
    + 	mmap_write_unlock(mm);
    + 	mmput(mm);
     @@ fs/userfaultfd.c: static int userfaultfd_register(struct userfaultfd_ctx *ctx,
      	unsigned long start, end, vma_end;
      	struct vma_iterator vmi;
    @@ fs/userfaultfd.c: static int userfaultfd_register(struct userfaultfd_ctx *ctx,
     -			/* vma_merge() invalidated the mas */
     -			vma = prev;
     -			goto next;
    -+		prev = vma_modify_flags_uffd(&vmi, prev, vma, start, vma_end,
    -+					     new_flags,
    -+					     (struct vm_userfaultfd_ctx){ctx});
    -+		if (IS_ERR(prev)) {
    -+			ret = PTR_ERR(prev);
    ++
    ++		vma = vma_modify_flags_uffd(&vmi, prev, vma, start, vma_end,
    ++					    new_flags,
    ++					    (struct vm_userfaultfd_ctx){ctx});
    ++		if (IS_ERR(vma)) {
    ++			ret = PTR_ERR(vma);
     +			break;
      		}
     -		if (vma->vm_start < start) {
    @@ fs/userfaultfd.c: static int userfaultfd_register(struct userfaultfd_ctx *ctx,
     -				break;
     -		}
     -	next:
    -+
    -+		if (prev)
    -+			vma = prev; /* vma_merge() invalidated the mas */
     +
      		/*
      		 * In the vma_merge() successful mprotect-like case 8:
    @@ fs/userfaultfd.c: static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
     -				 vma_policy(vma),
     -				 NULL_VM_UFFD_CTX, anon_vma_name(vma));
     -		if (prev) {
    -+		prev = vma_modify_flags_uffd(&vmi, prev, vma, start, vma_end,
    -+					     new_flags, NULL_VM_UFFD_CTX);
    -+		if (IS_ERR(prev)) {
    +-			vma = prev;
    +-			goto next;
    ++		vma = vma_modify_flags_uffd(&vmi, prev, vma, start, vma_end,
    ++					    new_flags, NULL_VM_UFFD_CTX);
    ++		if (IS_ERR(vma)) {
     +			ret = PTR_ERR(prev);
     +			break;
    -+		}
    -+
    -+		if (prev)
    - 			vma = prev;
    --			goto next;
    --		}
    + 		}
     -		if (vma->vm_start < start) {
     -			ret = split_vma(&vmi, vma, start, 1);
     -			if (ret)
    @@ fs/userfaultfd.c: static int userfaultfd_unregister(struct userfaultfd_ctx *ctx,
     -				break;
     -		}
     -	next:
    ++
      		/*
      		 * In the vma_merge() successful mprotect-like case 8:
      		 * the next vma was merged into the current one and
    @@ mm/madvise.c: static int madvise_update_vma(struct vm_area_struct *vma,
      	struct mm_struct *mm = vma->vm_mm;
      	int error;
     -	pgoff_t pgoff;
    -+	struct vm_area_struct *merged;
      	VMA_ITERATOR(vmi, mm, start);
      
      	if (new_flags == vma->vm_flags && anon_vma_name_eq(anon_vma_name(vma), anon_name)) {
    @@ mm/madvise.c: static int madvise_update_vma(struct vm_area_struct *vma,
     -		vma = *prev;
     -		goto success;
     -	}
    -+	merged = vma_modify_flags_name(&vmi, *prev, vma, start, end, new_flags,
    -+				       anon_name);
    -+	if (IS_ERR(merged))
    -+		return PTR_ERR(merged);
    ++	vma = vma_modify_flags_name(&vmi, *prev, vma, start, end, new_flags,
    ++				    anon_name);
    ++	if (IS_ERR(vma))
    ++		return PTR_ERR(vma);
      
    --	*prev = vma;
    -+	if (merged)
    -+		vma = *prev = merged;
    -+	else
    -+		*prev = vma;
    + 	*prev = vma;
      
     -	if (start != vma->vm_start) {
     -		error = split_vma(&vmi, vma, start, 1);
    @@ mm/madvise.c: static int madvise_update_vma(struct vm_area_struct *vma,
     
      ## mm/mempolicy.c ##
     @@ mm/mempolicy.c: static int mbind_range(struct vma_iterator *vmi, struct vm_area_struct *vma,
    + 		struct vm_area_struct **prev, unsigned long start,
    + 		unsigned long end, struct mempolicy *new_pol)
      {
    - 	struct vm_area_struct *merged;
    +-	struct vm_area_struct *merged;
      	unsigned long vmstart, vmend;
     -	pgoff_t pgoff;
     -	int err;
    @@ mm/mempolicy.c: static int mbind_range(struct vma_iterator *vmi, struct vm_area_
     -	merged = vma_merge(vmi, vma->vm_mm, *prev, vmstart, vmend, vma->vm_flags,
     -			 vma->anon_vma, vma->vm_file, pgoff, new_pol,
     -			 vma->vm_userfaultfd_ctx, anon_vma_name(vma));
    -+	merged =  vma_modify_policy(vmi, *prev, vma, vmstart, vmend, new_pol);
    -+	if (IS_ERR(merged))
    -+		return PTR_ERR(merged);
    -+
    - 	if (merged) {
    - 		*prev = merged;
    - 		return vma_replace_policy(merged, new_pol);
    - 	}
    - 
    +-	if (merged) {
    +-		*prev = merged;
    +-		return vma_replace_policy(merged, new_pol);
    +-	}
    +-
     -	if (vma->vm_start != vmstart) {
     -		err = split_vma(vmi, vma, vmstart, 1);
     -		if (err)
    @@ mm/mempolicy.c: static int mbind_range(struct vma_iterator *vmi, struct vm_area_
     -		if (err)
     -			return err;
     -	}
    --
    ++	vma =  vma_modify_policy(vmi, *prev, vma, vmstart, vmend, new_pol);
    ++	if (IS_ERR(vma))
    ++		return PTR_ERR(vma);
    + 
      	*prev = vma;
      	return vma_replace_policy(vma, new_pol);
    - }
     
      ## mm/mlock.c ##
     @@ mm/mlock.c: static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_struct *vma,
    @@ mm/mlock.c: static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_stru
      	int nr_pages;
      	int ret = 0;
      	vm_flags_t oldflags = vma->vm_flags;
    -+	struct vm_area_struct *merged;
    - 
    - 	if (newflags == oldflags || (oldflags & VM_SPECIAL) ||
    - 	    is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm) ||
     @@ mm/mlock.c: static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_struct *vma,
      		/* don't set VM_LOCKED or VM_LOCKONFAULT and don't count */
      		goto out;
    @@ mm/mlock.c: static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_stru
     -	if (*prev) {
     -		vma = *prev;
     -		goto success;
    -+	merged = vma_modify_flags(vmi, *prev, vma, start, end, newflags);
    -+	if (IS_ERR(merged)) {
    -+		ret = PTR_ERR(merged);
    ++	vma = vma_modify_flags(vmi, *prev, vma, start, end, newflags);
    ++	if (IS_ERR(vma)) {
    ++		ret = PTR_ERR(vma);
     +		goto out;
      	}
      
    @@ mm/mlock.c: static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_stru
     -		if (ret)
     -			goto out;
     -	}
    -+	if (merged)
    -+		vma = *prev = merged;
    - 
    +-
     -	if (end != vma->vm_end) {
     -		ret = split_vma(vmi, vma, end, 0);
     -		if (ret)
    @@ mm/mmap.c: int split_vma(struct vma_iterator *vmi, struct vm_area_struct *vma,
     + *
     + * If no merge is possible and the range does not span the entirety of the VMA,
     + * we then need to split the VMA to accommodate the change.
    ++ *
    ++ * The function returns either the merged VMA, the original VMA if a split was
    ++ * required instead, or an error if the split failed.
     + */
     +struct vm_area_struct *vma_modify(struct vma_iterator *vmi,
     +				  struct vm_area_struct *prev,
    @@ mm/mmap.c: int split_vma(struct vma_iterator *vmi, struct vm_area_struct *vma,
     +			return ERR_PTR(err);
     +	}
     +
    -+	return NULL;
    ++	return vma;
     +}
     +
      /*
    @@ mm/mprotect.c: mprotect_fixup(struct vma_iterator *vmi, struct mmu_gather *tlb,
      	unsigned int mm_cp_flags = 0;
      	unsigned long charged = 0;
     -	pgoff_t pgoff;
    -+	struct vm_area_struct *merged;
      	int error;
      
      	if (newflags == oldflags) {
    @@ mm/mprotect.c: mprotect_fixup(struct vma_iterator *vmi, struct mmu_gather *tlb,
     -			   vma->vm_userfaultfd_ctx, anon_vma_name(vma));
     -	if (*pprev) {
     -		vma = *pprev;
    -+	merged = vma_modify_flags(vmi, *pprev, vma, start, end, newflags);
    -+	if (IS_ERR(merged)) {
    -+		error = PTR_ERR(merged);
    -+		goto fail;
    -+	}
    -+
    -+	if (merged) {
    -+		vma = *pprev = merged;
    - 		VM_WARN_ON((vma->vm_flags ^ newflags) & ~VM_SOFTDIRTY);
    +-		VM_WARN_ON((vma->vm_flags ^ newflags) & ~VM_SOFTDIRTY);
     -		goto success;
    -+	} else {
    -+		*pprev = vma;
    ++	vma = vma_modify_flags(vmi, *pprev, vma, start, end, newflags);
    ++	if (IS_ERR(vma)) {
    ++		error = PTR_ERR(vma);
    ++		goto fail;
      	}
      
    --	*pprev = vma;
    --
    + 	*pprev = vma;
    + 
     -	if (start != vma->vm_start) {
     -		error = split_vma(vmi, vma, start, 1);
     -		if (error)
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1:  81f8e8008c9a ! 1:  05fce90455a2 mm: abstract the vma_merge()/split_vma() pattern for mprotect() et al.
────────────────┐
 Commit message │
────────────────┘
    parameters, create inline wrapper functions for each of the modify               │     parameters, create inline wrapper functions for each of the modify
    operations, parameterised only by what is required to perform the action.        │     operations, parameterised only by what is required to perform the action.
                                                                                     │ 
                                                                                     │     We can also significantly simplify the logic - by returning the VMA if we
                                                                                     │     split (or merged VMA if we do not) we no longer need specific handling for
                                                                                     │     merge/split cases in any of the call sites.
                                                                                     │ 
    Note that the userfaultfd_release() case works even though it does not           │     Note that the userfaultfd_release() case works even though it does not
    split VMAs - since start is set to vma->vm_start and end is set to               │     split VMAs - since start is set to vma->vm_start and end is set to
    vma->vm_end, the split logic does not trigger.                                   │     vma->vm_end, the split logic does not trigger.
────────────────┐
 Commit message │
────────────────┘
    - vma->vm_start) >> PAGE_SHIFT, and start - vma->vm_start will be 0 in this      │     - vma->vm_start) >> PAGE_SHIFT, and start - vma->vm_start will be 0 in this
    instance, this invocation will remain unchanged.                                 │     instance, this invocation will remain unchanged.
                                                                                     │ 
    Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>                              │ 
                                                                                     │     We eliminate a VM_WARN_ON() in mprotect_fixup() as this simply asserts that
                                                                                     │     vma_merge() correctly ensures that flags remain the same, something that is
                                                                                     │     already checked in is_mergeable_vma() and elsewhere, and in any case is not
                                                                                     │     specific to mprotect().
                                                                                     │ 
    Reviewed-by: Vlastimil Babka <vbabka@suse.cz>                                    │     Reviewed-by: Vlastimil Babka <vbabka@suse.cz>
                                                                                     │     Signed-off-by: Lorenzo Stoakes <lstoakes@gmail.com>
                                                                                     │ 
 ## fs/userfaultfd.c ##                                                              │  ## fs/userfaultfd.c ##
@@ fs/userfaultfd.c: static int userfaultfd_release(struct inode *inode, struct f↴   │ @@ fs/userfaultfd.c: static int userfaultfd_release(struct inode *inode, struct f↴
                                                                       …ile *file)   │                                                                        …ile *file)
──────────────────────────────────────────────────────────────────────────────────┐
 fs/userfaultfd.c: static int userfaultfd_release(struct inode *inode, struct fil │
──────────────────────────────────────────────────────────────────────────────────┘
-                 vma->vm_file, vma->vm_pgoff,                                       │ -                 vma->vm_file, vma->vm_pgoff,
-                 vma_policy(vma),                                                   │ -                 vma_policy(vma),
-                 NULL_VM_UFFD_CTX, anon_vma_name(vma));                             │ -                 NULL_VM_UFFD_CTX, anon_vma_name(vma));
                                                                                     │ -        if (prev) {
                                                                                     │ -            vma = prev;
                                                                                     │ -        } else {
                                                                                     │ -            prev = vma;
                                                                                     │ -        }
+        prev = vma_modify_flags_uffd(&vmi, prev, vma, vma->vm_start,                │ +        vma = vma_modify_flags_uffd(&vmi, prev, vma, vma->vm_start,
+                         vma->vm_end, new_flags,                                    │ +                        vma->vm_end, new_flags,
+                         NULL_VM_UFFD_CTX);                                         │ +                        NULL_VM_UFFD_CTX);
                                                                                     │  
                                                                                     │          vma_start_write(vma);
                                                                                     │          userfaultfd_set_vm_flags(vma, new_flags);
                                                                                     │          vma->vm_userfaultfd_ctx = NULL_VM_UFFD_CTX;
+                                                                                    │ +
         if (prev) {                                                                 │ 
             vma = prev;                                                             │ 
         } else {                                                                    │ 
                                                                                     │ +        prev = vma;
                                                                                     │      }
                                                                                     │      mmap_write_unlock(mm);
                                                                                     │      mmput(mm);
@@ fs/userfaultfd.c: static int userfaultfd_register(struct userfaultfd_ctx *ctx,    │ @@ fs/userfaultfd.c: static int userfaultfd_register(struct userfaultfd_ctx *ctx,
     unsigned long start, end, vma_end;                                              │      unsigned long start, end, vma_end;
     struct vma_iterator vmi;                                                        │      struct vma_iterator vmi;
────────────────────────────────────────────────────────────────────────────────┐
 fs/userfaultfd.c: static int userfaultfd_register(struct userfaultfd_ctx *ctx, │
────────────────────────────────────────────────────────────────────────────────┘
-            /* vma_merge() invalidated the mas */                                   │ -            /* vma_merge() invalidated the mas */
-            vma = prev;                                                             │ -            vma = prev;
-            goto next;                                                              │ -            goto next;
                                                                                     │ +
+        prev = vma_modify_flags_uffd(&vmi, prev, vma, start, vma_end,               │ +        vma = vma_modify_flags_uffd(&vmi, prev, vma, start, vma_end,
+                         new_flags,                                                 │ +                        new_flags,
+                         (struct vm_userfaultfd_ctx){ctx});                         │ +                        (struct vm_userfaultfd_ctx){ctx});
+        if (IS_ERR(prev)) {                                                         │ +        if (IS_ERR(vma)) {
+            ret = PTR_ERR(prev);                                                    │ +            ret = PTR_ERR(vma);
+            break;                                                                  │ +            break;
         }                                                                           │          }
-        if (vma->vm_start < start) {                                                │ -        if (vma->vm_start < start) {
────────────────────────────────────────────────────────────────────────────────┐
 fs/userfaultfd.c: static int userfaultfd_register(struct userfaultfd_ctx *ctx, │
────────────────────────────────────────────────────────────────────────────────┘
-                break;                                                              │ -                break;
-        }                                                                           │ -        }
-    next:                                                                           │ -    next:
+                                                                                    │ 
+        if (prev)                                                                   │ 
+            vma = prev; /* vma_merge() invalidated the mas */                       │ 
+                                                                                    │ +
         /*                                                                          │          /*
          * In the vma_merge() successful mprotect-like case 8:                      │           * In the vma_merge() successful mprotect-like case 8:
──────────────────────────────────────────────────────────────────────────────────┐
 fs/userfaultfd.c: static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, │
──────────────────────────────────────────────────────────────────────────────────┘
-                 vma_policy(vma),                                                   │ -                 vma_policy(vma),
-                 NULL_VM_UFFD_CTX, anon_vma_name(vma));                             │ -                 NULL_VM_UFFD_CTX, anon_vma_name(vma));
-        if (prev) {                                                                 │ -        if (prev) {
                                                                                     │ -            vma = prev;
                                                                                     │ -            goto next;
+        prev = vma_modify_flags_uffd(&vmi, prev, vma, start, vma_end,               │ +        vma = vma_modify_flags_uffd(&vmi, prev, vma, start, vma_end,
+                         new_flags, NULL_VM_UFFD_CTX);                              │ +                        new_flags, NULL_VM_UFFD_CTX);
+        if (IS_ERR(prev)) {                                                         │ +        if (IS_ERR(vma)) {
+            ret = PTR_ERR(prev);                                                    │ +            ret = PTR_ERR(prev);
+            break;                                                                  │ +            break;
+        }                                                                           │          }
+                                                                                    │ 
+        if (prev)                                                                   │ 
             vma = prev;                                                             │ 
-            goto next;                                                              │ 
-        }                                                                           │ 
-        if (vma->vm_start < start) {                                                │ -        if (vma->vm_start < start) {
-            ret = split_vma(&vmi, vma, start, 1);                                   │ -            ret = split_vma(&vmi, vma, start, 1);
-            if (ret)                                                                │ -            if (ret)
──────────────────────────────────────────────────────────────────────────────────┐
 fs/userfaultfd.c: static int userfaultfd_unregister(struct userfaultfd_ctx *ctx, │
──────────────────────────────────────────────────────────────────────────────────┘
-                break;                                                              │ -                break;
-        }                                                                           │ -        }
-    next:                                                                           │ -    next:
                                                                                     │ +
         /*                                                                          │          /*
          * In the vma_merge() successful mprotect-like case 8:                      │           * In the vma_merge() successful mprotect-like case 8:
          * the next vma was merged into the current one and                         │           * the next vma was merged into the current one and
─────────────────────────────────────────────────────────────────────────┐
 mm/madvise.c: static int madvise_update_vma(struct vm_area_struct *vma, │
─────────────────────────────────────────────────────────────────────────┘
     struct mm_struct *mm = vma->vm_mm;                                              │      struct mm_struct *mm = vma->vm_mm;
     int error;                                                                      │      int error;
-    pgoff_t pgoff;                                                                  │ -    pgoff_t pgoff;
+    struct vm_area_struct *merged;                                                  │ 
     VMA_ITERATOR(vmi, mm, start);                                                   │      VMA_ITERATOR(vmi, mm, start);
                                                                                     │  
     if (new_flags == vma->vm_flags && anon_vma_name_eq(anon_vma_name(vma), anon_↴   │      if (new_flags == vma->vm_flags && anon_vma_name_eq(anon_vma_name(vma), anon_↴
                                                                         …name)) {   │                                                                          …name)) {
─────────────────────────────────────────────────────────────────────────┐
 mm/madvise.c: static int madvise_update_vma(struct vm_area_struct *vma, │
─────────────────────────────────────────────────────────────────────────┘
-        vma = *prev;                                                                │ -        vma = *prev;
-        goto success;                                                               │ -        goto success;
-    }                                                                               │ -    }
+    merged = vma_modify_flags_name(&vmi, *prev, vma, start, end, new_flags,         │ +    vma = vma_modify_flags_name(&vmi, *prev, vma, start, end, new_flags,
+                       anon_name);                                                  │ +                    anon_name);
+    if (IS_ERR(merged))                                                             │ +    if (IS_ERR(vma))
+        return PTR_ERR(merged);                                                     │ +        return PTR_ERR(vma);
                                                                                     │  
-    *prev = vma;                                                                    │      *prev = vma;
+    if (merged)                                                                     │ 
+        vma = *prev = merged;                                                       │ 
+    else                                                                            │ 
+        *prev = vma;                                                                │ 
                                                                                     │  
-    if (start != vma->vm_start) {                                                   │ -    if (start != vma->vm_start) {
-        error = split_vma(&vmi, vma, start, 1);                                     │ -        error = split_vma(&vmi, vma, start, 1);
─────────────────────────────────────────────────────────────────────────┐
 mm/madvise.c: static int madvise_update_vma(struct vm_area_struct *vma, │
─────────────────────────────────────────────────────────────────────────┘
                                                                                     │ 
 ## mm/mempolicy.c ##                                                                │  ## mm/mempolicy.c ##
@@ mm/mempolicy.c: static int mbind_range(struct vma_iterator *vmi, struct vm_are↴   │ @@ mm/mempolicy.c: static int mbind_range(struct vma_iterator *vmi, struct vm_are↴
                                                                   …a_struct *vma,   │                                                                    …a_struct *vma,
                                                                                     │          struct vm_area_struct **prev, unsigned long start,
                                                                                     │          unsigned long end, struct mempolicy *new_pol)
 {                                                                                   │  {
     struct vm_area_struct *merged;                                                  │ -    struct vm_area_struct *merged;
     unsigned long vmstart, vmend;                                                   │      unsigned long vmstart, vmend;
-    pgoff_t pgoff;                                                                  │ -    pgoff_t pgoff;
-    int err;                                                                        │ -    int err;
──────────────────────────────────────────────────────────────────────────────────┐
 mm/mempolicy.c: static int mbind_range(struct vma_iterator *vmi, struct vm_area_ │
──────────────────────────────────────────────────────────────────────────────────┘
-    merged = vma_merge(vmi, vma->vm_mm, *prev, vmstart, vmend, vma->vm_flags,       │ -    merged = vma_merge(vmi, vma->vm_mm, *prev, vmstart, vmend, vma->vm_flags,
-             vma->anon_vma, vma->vm_file, pgoff, new_pol,                           │ -             vma->anon_vma, vma->vm_file, pgoff, new_pol,
-             vma->vm_userfaultfd_ctx, anon_vma_name(vma));                          │ -             vma->vm_userfaultfd_ctx, anon_vma_name(vma));
+    merged =  vma_modify_policy(vmi, *prev, vma, vmstart, vmend, new_pol);          │ 
+    if (IS_ERR(merged))                                                             │ -    if (merged) {
                                                                                     │ -        *prev = merged;
+        return PTR_ERR(merged);                                                     │ -        return vma_replace_policy(merged, new_pol);
+                                                                                    │ 
     if (merged) {                                                                   │ 
         *prev = merged;                                                             │ 
         return vma_replace_policy(merged, new_pol);                                 │ 
     }                                                                               │ -    }
                                                                                     │ 
                                                                                     │ -
-    if (vma->vm_start != vmstart) {                                                 │ -    if (vma->vm_start != vmstart) {
-        err = split_vma(vmi, vma, vmstart, 1);                                      │ -        err = split_vma(vmi, vma, vmstart, 1);
-        if (err)                                                                    │ -        if (err)
──────────────────────────────────────────────────────────────────────────────────┐
 mm/mempolicy.c: static int mbind_range(struct vma_iterator *vmi, struct vm_area_ │
──────────────────────────────────────────────────────────────────────────────────┘
-        if (err)                                                                    │ -        if (err)
-            return err;                                                             │ -            return err;
-    }                                                                               │ -    }
-                                                                                    │ 
                                                                                     │ +    vma =  vma_modify_policy(vmi, *prev, vma, vmstart, vmend, new_pol);
                                                                                     │ +    if (IS_ERR(vma))
                                                                                     │ +        return PTR_ERR(vma);
                                                                                     │  
     *prev = vma;                                                                    │      *prev = vma;
     return vma_replace_policy(vma, new_pol);                                        │      return vma_replace_policy(vma, new_pol);
 }                                                                                   │ 
                                                                                     │ 
 ## mm/mlock.c ##                                                                    │  ## mm/mlock.c ##
@@ mm/mlock.c: static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_st↴   │ @@ mm/mlock.c: static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_st↴
                                                                       …ruct *vma,   │                                                                        …ruct *vma,
──────────────────────────────────────────────────────────────────────────────────┐
 mm/mlock.c: static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_stru │
──────────────────────────────────────────────────────────────────────────────────┘
     int nr_pages;                                                                   │      int nr_pages;
     int ret = 0;                                                                    │      int ret = 0;
     vm_flags_t oldflags = vma->vm_flags;                                            │      vm_flags_t oldflags = vma->vm_flags;
+    struct vm_area_struct *merged;                                                  │ 
                                                                                     │ 
     if (newflags == oldflags || (oldflags & VM_SPECIAL) ||                          │ 
         is_vm_hugetlb_page(vma) || vma == get_gate_vma(current->mm) ||              │ 
@@ mm/mlock.c: static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_st↴   │ @@ mm/mlock.c: static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_st↴
                                                                       …ruct *vma,   │                                                                        …ruct *vma,
         /* don't set VM_LOCKED or VM_LOCKONFAULT and don't count */                 │          /* don't set VM_LOCKED or VM_LOCKONFAULT and don't count */
         goto out;                                                                   │          goto out;
──────────────────────────────────────────────────────────────────────────────────┐
 mm/mlock.c: static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_stru │
──────────────────────────────────────────────────────────────────────────────────┘
-    if (*prev) {                                                                    │ -    if (*prev) {
-        vma = *prev;                                                                │ -        vma = *prev;
-        goto success;                                                               │ -        goto success;
+    merged = vma_modify_flags(vmi, *prev, vma, start, end, newflags);               │ +    vma = vma_modify_flags(vmi, *prev, vma, start, end, newflags);
+    if (IS_ERR(merged)) {                                                           │ +    if (IS_ERR(vma)) {
+        ret = PTR_ERR(merged);                                                      │ +        ret = PTR_ERR(vma);
+        goto out;                                                                   │ +        goto out;
     }                                                                               │      }
                                                                                     │  
──────────────────────────────────────────────────────────────────────────────────┐
 mm/mlock.c: static int mlock_fixup(struct vma_iterator *vmi, struct vm_area_stru │
──────────────────────────────────────────────────────────────────────────────────┘
-        if (ret)                                                                    │ -        if (ret)
-            goto out;                                                               │ -            goto out;
-    }                                                                               │ -    }
+    if (merged)                                                                     │ 
+        vma = *prev = merged;                                                       │ 
                                                                                     │ 
                                                                                     │ -
-    if (end != vma->vm_end) {                                                       │ -    if (end != vma->vm_end) {
-        ret = split_vma(vmi, vma, end, 0);                                          │ -        ret = split_vma(vmi, vma, end, 0);
-        if (ret)                                                                    │ -        if (ret)
────────────────────────────────────────────────────────────────────────────────┐
 mm/mmap.c: int split_vma(struct vma_iterator *vmi, struct vm_area_struct *vma, │
────────────────────────────────────────────────────────────────────────────────┘
+ *                                                                                  │ + *
+ * If no merge is possible and the range does not span the entirety of the VMA,     │ + * If no merge is possible and the range does not span the entirety of the VMA,
+ * we then need to split the VMA to accommodate the change.                         │ + * we then need to split the VMA to accommodate the change.
                                                                                     │ + *
                                                                                     │ + * The function returns either the merged VMA, the original VMA if a split was
                                                                                     │ + * required instead, or an error if the split failed.
+ */                                                                                 │ + */
+struct vm_area_struct *vma_modify(struct vma_iterator *vmi,                         │ +struct vm_area_struct *vma_modify(struct vma_iterator *vmi,
+                  struct vm_area_struct *prev,                                      │ +                  struct vm_area_struct *prev,
────────────────────────────────────────────────────────────────────────────────┐
 mm/mmap.c: int split_vma(struct vma_iterator *vmi, struct vm_area_struct *vma, │
────────────────────────────────────────────────────────────────────────────────┘
+            return ERR_PTR(err);                                                    │ +            return ERR_PTR(err);
+    }                                                                               │ +    }
+                                                                                    │ +
+    return NULL;                                                                    │ +    return vma;
+}                                                                                   │ +}
+                                                                                    │ +
 /*                                                                                  │  /*
─────────────────────────────────────────────────────────────────────────────────┐
 mm/mprotect.c: mprotect_fixup(struct vma_iterator *vmi, struct mmu_gather *tlb, │
─────────────────────────────────────────────────────────────────────────────────┘
     unsigned int mm_cp_flags = 0;                                                   │      unsigned int mm_cp_flags = 0;
     unsigned long charged = 0;                                                      │      unsigned long charged = 0;
-    pgoff_t pgoff;                                                                  │ -    pgoff_t pgoff;
+    struct vm_area_struct *merged;                                                  │ 
     int error;                                                                      │      int error;
                                                                                     │  
     if (newflags == oldflags) {                                                     │      if (newflags == oldflags) {
─────────────────────────────────────────────────────────────────────────────────┐
 mm/mprotect.c: mprotect_fixup(struct vma_iterator *vmi, struct mmu_gather *tlb, │
─────────────────────────────────────────────────────────────────────────────────┘
-               vma->vm_userfaultfd_ctx, anon_vma_name(vma));                        │ -               vma->vm_userfaultfd_ctx, anon_vma_name(vma));
-    if (*pprev) {                                                                   │ -    if (*pprev) {
-        vma = *pprev;                                                               │ -        vma = *pprev;
+    merged = vma_modify_flags(vmi, *pprev, vma, start, end, newflags);              │ 
+    if (IS_ERR(merged)) {                                                           │ 
+        error = PTR_ERR(merged);                                                    │ 
+        goto fail;                                                                  │ 
+    }                                                                               │ 
+                                                                                    │ 
+    if (merged) {                                                                   │ 
+        vma = *pprev = merged;                                                      │ 
         VM_WARN_ON((vma->vm_flags ^ newflags) & ~VM_SOFTDIRTY);                     │ -        VM_WARN_ON((vma->vm_flags ^ newflags) & ~VM_SOFTDIRTY);
-        goto success;                                                               │ -        goto success;
+    } else {                                                                        │ 
+        *pprev = vma;                                                               │ 
                                                                                     │ +    vma = vma_modify_flags(vmi, *pprev, vma, start, end, newflags);
                                                                                     │ +    if (IS_ERR(vma)) {
                                                                                     │ +        error = PTR_ERR(vma);
                                                                                     │ +        goto fail;
     }                                                                               │      }
                                                                                     │  
-    *pprev = vma;                                                                   │      *pprev = vma;
-                                                                                    │ 
                                                                                     │  
-    if (start != vma->vm_start) {                                                   │ -    if (start != vma->vm_start) {
-        error = split_vma(vmi, vma, start, 1);                                      │ -        error = split_vma(vmi, vma, start, 1);
-        if (error)                                                                  │ -        if (error)
Implementation
Ideally, this side-by-side view of git range-diff would
just appear after I configure pager.range-diff='delta -s' in
.gitconfig. Unfortunately, it’s not that easy:
- delta can’t parse the output of git range-diff
- that output isn’t even meant to be machine-readable
Nevertheless, I hacked together a proof-of-concept preprocessor that takes the
output of git range-diff and turns it into something that
delta parses and presents nicely to a human:
#!/usr/bin/env bash
# preprocessor for "git range-diff" output to be fed into "delta" for side-by-side diff
# see ~/.config/git/config for usage (si-range-diff alias)
#
# depends on ansi2txt from https://github.com/kilobyte/colorized-logs
#
# developed against git 2.40 - git range-diff doesn't have stable output, might need adjustments
# see https://github.com/git/git/blob/ee48e70a829d1fa2da82f14787051ad8e7c45b71/range-diff.c#L376
set -eu
coproc ansi2txt {
	stdbuf -oL ansi2txt
}
while IFS= read -r l; do
	# decolor line for matching/processing
	printf "%s\n" "$l" >&"${ansi2txt[1]}"
	IFS= read -r ll <&"${ansi2txt[0]}"
	if [[ $ll == "    @@ "* ]]; then # hunk header line
		# fake hunk header for delta
		printf "@@ -0,0 +0,0 @@ %s\n" "${ll#    @@ }"
	elif [[ $ll == "    "* ]]; then # hunk diff line
		# un-indent diff
		printf "%s\n" "${ll#    }"
	else # pair header line
		# horizontal separator before the line
		tput setaf 39; tput setab 235; printf %"$(tput cols)"s | sed 's/ /─/g'; tput sgr0
		printf "%s\n" "$l"
		if [[ $ll =~ ^[[:digit:]][[:digit:]]*:"  "([[:alnum:]][[:alnum:]]*)" ! "[[:digit:]][[:digit:]]*:"  "([[:alnum:]][[:alnum:]]*)" " ]]; then
			# fake file header for delta to syntax highlight as patch
			echo "--- ${BASH_REMATCH[1]}.patch"
			echo "+++ ${BASH_REMATCH[2]}.patch"
		else
			# extra line for unmatched commits
			echo
		fi
	fi
done
To use this, override pager.range-diff when invoking git range-diff, like
so:
$ git -c pager.range-diff='
        git-range-diff-delta-preproc \
        | delta \
                --side-by-side \
                --file-style=omit \
                --line-numbers-left-format="" \
                --line-numbers-right-format="│ " \
                --hunk-header-style=syntax \
        ' range-diff wx-partial...wx-partial-rebase
(Or just steal all the delta-related bits from my git config.)
Yeah, I know, it’s a massive hack. 
Next steps
To make this easier to use for the general public, we’d need:
- machine-readable output from git range-diff(other git commands have that, e.g.git status --porcelain)
- implement parsing of that output in delta
Unfortunately, I can’t volunteer to do either at this point2, apologies.