Compare files in a human-friendly way using an experimental difftastic utility.
This utility is built using Rust, so I will use rustup snap.
$ snap info rustup
name: rustup summary: "EXPERIMENTAL: The Rust Language installer" publisher: Daniel Silverstone (dsilvers) store-url: https://snapcraft.io/rustup contact: dsilvers@digital-scurf.org license: Apache-2.0 OR MIT description: | **NOTA BENE:** _This is an experimental, unofficial, snap and should not be relied on._ Rustup is the Rust Language's installer and primary front-end. You probably want this if you want to develop anything written in Rust. Please note, this snap is experimental and may be yanked without notice if this experiment proves to be a bad idea. snap-id: tO4cnls38mXDXJiA0U72G7eCyrCAMweL channels: latest/stable: 1.24.3 2021-06-22 (1027) 4MB classic latest/candidate: ^ latest/beta: 1.24.3 2021-05-31 (1027) 4MB classic latest/edge: 1.24.3 2022-04-01 (1350) 5MB classic
Install the Rust toolchain installer.
$ sudo snap install --classic rustup
rustup 1.24.3 from Daniel Silverstone (dsilvers) installed
Inspect available commands.
$ rustup
rustup 1.24.3 (ce5817a94 2021-05-31) The Rust toolchain installer USAGE: rustup [FLAGS] [+toolchain] <SUBCOMMAND> FLAGS: -v, --verbose Enable verbose output -q, --quiet Disable progress output -h, --help Prints help information -V, --version Prints version information ARGS: <+toolchain> release channel (e.g. +stable) or custom toolchain to set override SUBCOMMANDS: show Show the active and installed toolchains or profiles update Update Rust toolchains and rustup check Check for updates to Rust toolchains and rustup default Set the default toolchain toolchain Modify or query the installed toolchains target Modify a toolchain's supported targets component Modify a toolchain's installed components override Modify directory toolchain overrides run Run a command with an environment configured for a given toolchain which Display which binary will be run for a given command doc Open the documentation for the current toolchain man View the man page for a given command self Modify the rustup installation set Alter rustup settings completions Generate tab-completion scripts for your shell help Prints this message or the help of the given subcommand(s) DISCUSSION: Rustup installs The Rust Programming Language from the official release channels, enabling you to easily switch between stable, beta, and nightly compilers and keep them updated. It makes cross-compiling simpler with binary builds of the standard library for common platforms. If you are new to Rust consider running `rustup doc --book` to learn Rust.
By default there will be no active toolchain.
$ rustup show
Default host: x86_64-unknown-linux-gnu rustup home: /home/milosz/snap/rustup/common/rustup no active toolchain
Install Rust toolchain using stable channel.
$ rustup toolchain install stable
info: syncing channel updates for 'stable-x86_64-unknown-linux-gnu' info: latest update on 2022-02-24, rust version 1.59.0 (9d1b2106e 2022-02-23) info: downloading component 'cargo' info: downloading component 'clippy' info: downloading component 'rust-docs' info: downloading component 'rust-std' info: downloading component 'rustc' info: downloading component 'rustfmt' info: installing component 'cargo' info: installing component 'clippy' info: installing component 'rust-docs' info: installing component 'rust-std' info: installing component 'rustc' info: installing component 'rustfmt'
Display current toolchain.
$ rustup show
Default host: x86_64-unknown-linux-gnu rustup home: /home/milosz/snap/rustup/common/rustup stable-x86_64-unknown-linux-gnu (default) rustc 1.59.0 (9d1b2106e 2022-02-23)
Install difftastic
.
$ cargo install difftastic
Updating crates.io index Downloaded difftastic v0.25.0 Downloaded 1 crate (6.2 MB) in 1.49s Installing difftastic v0.25.0 Downloaded archery v0.4.0 Downloaded env_logger v0.7.1 Downloaded const_format v0.2.22 Downloaded mimalloc v0.1.28 Downloaded const_format_proc_macros v0.2.22 Downloaded owo-colors v3.3.0 Downloaded pretty_env_logger v0.4.0 Downloaded term_size v0.3.2 Downloaded typed-arena v2.0.1 Downloaded radix-heap v0.4.2 Downloaded wu-diff v0.1.2 Downloaded tree-sitter v0.20.6 Downloaded rpds v0.10.0 Downloaded libmimalloc-sys v0.1.24 Downloaded num_cpus v1.13.1 Downloaded os_str_bytes v6.0.0 Downloaded quote v1.0.17 Downloaded quick-error v1.2.3 Downloaded rustc-hash v1.1.0 Downloaded indexmap v1.8.1 Downloaded crossbeam-utils v0.8.8 Downloaded memchr v2.4.1 Downloaded crossbeam-epoch v0.9.8 Downloaded aho-corasick v0.7.18 Downloaded lazy_static v1.4.0 Downloaded regex v1.5.5 Downloaded crossbeam-channel v0.5.4 Downloaded clap v3.1.8 Downloaded scopeguard v1.1.0 Downloaded same-file v1.0.6 Downloaded strsim v0.10.0 Downloaded walkdir v2.3.2 Downloaded proc-macro2 v1.0.36 Downloaded textwrap v0.15.0 Downloaded termcolor v1.1.3 Downloaded either v1.6.1 Downloaded rayon-core v1.9.1 Downloaded crossbeam-deque v0.8.1 Downloaded cfg-if v1.0.0 Downloaded atty v0.2.14 Downloaded regex-syntax v0.6.25 Downloaded memoffset v0.6.5 Downloaded unicode-xid v0.2.2 Downloaded humantime v1.3.0 Downloaded hashbrown v0.11.2 Downloaded itertools v0.10.3 Downloaded static_assertions v1.1.0 Downloaded log v0.4.16 Downloaded cc v1.0.73 Downloaded autocfg v1.1.0 Downloaded bitflags v1.3.2 Downloaded rayon v1.5.1 Downloaded libc v0.2.121 Downloaded 53 crates (4.2 MB) in 0.87s (largest was `libmimalloc-sys` at 1.1 MB) Compiling autocfg v1.1.0 Compiling libc v0.2.121 Compiling crossbeam-utils v0.8.8 Compiling cc v1.0.73 Compiling memchr v2.4.1 Compiling lazy_static v1.4.0 Compiling cfg-if v1.0.0 Compiling rayon-core v1.9.1 Compiling scopeguard v1.1.0 Compiling proc-macro2 v1.0.36 Compiling log v0.4.16 Compiling regex-syntax v0.6.25 Compiling unicode-xid v0.2.2 Compiling either v1.6.1 Compiling quick-error v1.2.3 Compiling termcolor v1.1.3 Compiling hashbrown v0.11.2 Compiling static_assertions v1.1.0 Compiling strsim v0.10.0 Compiling textwrap v0.15.0 Compiling bitflags v1.3.2 Compiling same-file v1.0.6 Compiling wu-diff v0.1.2 Compiling typed-arena v2.0.1 Compiling owo-colors v3.3.0 Compiling rustc-hash v1.1.0 Compiling radix-heap v0.4.2 Compiling memoffset v0.6.5 Compiling crossbeam-epoch v0.9.8 Compiling rayon v1.5.1 Compiling indexmap v1.8.1 Compiling humantime v1.3.0 Compiling archery v0.4.0 Compiling itertools v0.10.3 Compiling walkdir v2.3.2 Compiling libmimalloc-sys v0.1.24 Compiling tree-sitter v0.20.6 Compiling rpds v0.10.0 Compiling crossbeam-channel v0.5.4 Compiling aho-corasick v0.7.18 Compiling os_str_bytes v6.0.0 Compiling num_cpus v1.13.1 Compiling atty v0.2.14 Compiling term_size v0.3.2 Compiling quote v1.0.17 Compiling regex v1.5.5 Compiling crossbeam-deque v0.8.1 Compiling clap v3.1.8 Compiling const_format_proc_macros v0.2.22 Compiling mimalloc v0.1.28 Compiling env_logger v0.7.1 Compiling pretty_env_logger v0.4.0 Compiling const_format v0.2.22 Compiling difftastic v0.25.0 Finished release [optimized] target(s) in 1m 00s Installing /home/milosz/.cargo/bin/difft Installed package `difftastic v0.25.0` (executable `difft`)
Extend PATH
variable and update ~/.bashrc
file accordingly.
$ export PATH="$PATH:/home/milosz/.cargo/bin/"
Display usage information.
$ difft
Difftastic 0.25.0 Wilfred Hughes <me@wilfred.me.uk> A diff that understands syntax USAGE: difft [OPTIONS] OLD-PATH NEW-PATH OPTIONS: --background <BACKGROUND> Set the background brightness. Overrides $DFT_BACKGROUND if present. Difftastic will prefer brighter colours on dark backgrounds. [possible values: dark, light] --byte-limit <LIMIT> Use a text diff if either input file exceeds this size. Overrides $DFT_BYTE_LIMIT if present. [default: 1000000] --color <WHEN> When to use color output. [possible values: always, auto, never] -h, --help Print help information --missing-as-empty Treat paths that don';t exist as equivalent to an empty file. --node-limit <LIMIT> Use a text diff if the number of syntax nodes exceeds this number. Overrides $DFT_NODE_LIMIT if present. [default: 30000] --skip-unchanged Don';t display anything if a file is unchanged. -V, --version Print version information --width <COLUMNS> Use this many columns when calculating line wrapping. Overrides $DFT_WIDTH if present. If not specified, difftastic will detect the terminal width. DEBUG OPTIONS: --dump-syntax <PATH> Parse a single file with tree-sitter and display the difftastic syntax tree. --dump-ts <PATH> Parse a single file with tree-sitter and display the tree-sitter parse tree.
Define difftastic
as an external diff utility for git.
$ git config --global diff.external difft
Sample usage.
greasemonkey-gitlab-scoped-labels on main via v12.22.5 ❯ git log -p -n 3 --ext-diff commit 8bf864710ac48d5530937ff16bb3f91932f12f0f (HEAD -> main, tag: version_11, origin/main, origin/HEAD) Author: Milosz Galazka <milosz@sleeplessbeastie.eu> Date: Mon Mar 14 19:56:08 2022 +0100 Version update gitlab-scoped-labels.js --- JavaScript 1 // ==UserScript== 1 // ==UserScript== 2 // @name GitLab scoped labels (view only) 2 // @name GitLab scoped labels (view only) 3 // @version 10 3 // @version 11 4 // @grant none 4 // @grant none 5 // @include https://git.octocat.lab/* 5 // @include https://git.octocat.lab/* 6 // @license GPL-3.0 License; https://www.gnu.org/li 6 // @license GPL-3.0 License; https://www.gnu.org/lic censes/gpl-3.0.txt enses/gpl-3.0.txt commit 5ff891177ed3bfaaf20c6d6376036d028e7f48a0 Merge: 11c1570 4397cfe Author: Milosz Galazka <milosz@sleeplessbeastie.eu> Date: Mon Mar 14 19:54:39 2022 +0100 Merge pull request #5 from baubie/main Add scoped label styling to merge request pages. commit 4397cfe5b2747e10f3e58c0d83530ffe1e7e99e3 Author: Brandon Aubie <brandon@aubie.ca> Date: Mon Mar 14 12:17:16 2022 -0400 Add scoped label styling to merge request pages. gitlab-scoped-labels.js --- JavaScript 72 72 label.innerHTML = label.innerText.replace(/([^:]*)::([^:]*)/, "<span style=';background-color: " + color + "'; class=';gl-label-text gl-label-text-light';>$1</span> <span class=';gl-label-text-scoped';>$2</span>"); 73 73 } 74 74 } 75 75 } else if(window.location.pathname.match(/.*\/merge_requests$/)) { // merge request list 76 const labels = document.querySelectorAll("span.gl-label"); 77 for(const label of labels) { 78 if(label.innerText.includes("::")) { 79 label.classList.add("gl-label-scoped") 80 const color = label.innerHTML.replace(/<.*style="background-color: (#\w*)".*/, "$1") 81 label.setAttribute("style", label.getAttribute("style") + "--label-background-color: " + color + "; --label-inset-border: inset 0 0 0 2px " + color + "; color " + color + ";") 82 label.innerHTML = label.innerText.replace(/([^:]*)::([^:]*)/, "<span style=';background-color: " + color + "'; class=';gl-label-text gl-label-text-light';>$1</span> <span class=';gl-label-text-scoped';>$2</span>"); 83 } 84 } 85 } else if(window.location.pathname.match(/.*\/merge_requests\/.*/)) { // merge request 86 const labels = document.querySelectorAll("span.gl-label"); 87 for(const label of labels) { 88 if(label.innerText.includes("::")) { 89 label.classList.add("gl-label-scoped") 90 const color = label.innerHTML.replace(/<.*style="background-color: (#\w*)".*/, "$1") 91 label.setAttribute("style", label.getAttribute("style") + "--label-background-color: " + color + "; --label-inset-border: inset 0 0 0 2px " + color + "; color " + color + ";") 92 label.innerHTML = label.innerText.replace(/([^:]*)::([^:]*)/, "<span style=';background-color: " + color + "'; class=';gl-label-text gl-label-text-light';>$1</span> <span class=';gl-label-text-scoped';>$2</span>"); 93 } 94 } 95 const callback = function(mutations, observer) { 96 for(const mutation of mutations) { 97 if(mutation.target.classList.contains("notes")) { 98 for(const timeline of mutation.addedNodes) { 99 const labels = timeline.querySelectorAll("span.gl-label") 100 for(const label of labels) { 101 if(label.innerText.includes("::")) { 102 label.classList.add("gl-label-scoped") 103 label.firstChild.innerHTML.replace(/<.* style="background-color: (#\w*)".*/, "--label-background-color: $1; --label-inset-border: inset 0 0 0 1px $1;") 104 label.setAttribute("style", label.getAttribute("style") + label.firstChild.innerHTML.replace(/<.* style="background-color: (#\w*)".*/, "--label-background-color: $1; --label-inset-border: inset 0 0 0 1px $1;")) 105 label.firstChild.innerHTML = label.firstChild.innerHTML.replace(/<(.*)>([^:]*)::([^:]*)<\/span>/, "<$1>$2</span> <span class=';gl-label-text-scoped';>$3</span>") 106 } 107 } 108 } 109 } 110 } 111 } 112 const observer = new MutationObserver(callback) 113 observer.observe(document, { childList: true, subtree: true }) 114 }
greasemonkey-gitlab-scoped-labels on main via v12.22.5 ➜ git diff HEAD~3 HEAD~4 gitlab-scoped-labels.js --- JavaScript 17 17 label.classList.add("gl-label-scoped") 18 18 label.innerHTML = label.innerText.replace(/([^:]*)::([^:]*)/, "<a href=';#'; class=';gl-link gl-label-link';><span class=';gl-label-text';>$1</span> <span class=';gl-label-text-scoped';>$2</span></a>"); 19 19 } else if(label.parentNode.classList.contains("board-title-text")) { .. 20 console.log(label) 20 21 label.classList.add("gl-label-scoped") 21 22 } 22 23 }
crio-top on main via v1.17 ➜ git diff HEAD~2 HEAD~4 Makefile --- Text 5 go run src/crio-top/main.go --configuration exa 5 go run src/crio-top/main.go --configuration exam . mples/configuration.yaml . ples/configuration.yaml 6 6 7 test: 7 test: 8 go test ./src/background ./src/configuration ./ 8 go test ./src/... . src/terminal . 9 9 10 coverage: 10 coverage: 11 go test -cover ./src/background ./src/configura 11 go test -cover ./src/... .. tion ./src/terminal .. 12 12 13 all: build 13 all: build readme.md --- Text 31 31 header: 32 32 width: 33 33 server: 20 .. 34 status: true 34 35 refresh: 35 36 window: 2 36 37 data: 2
Inspect Difftastic manual for more details.