Repository: dandavison/delta
Pull request #610 (2021-05-25 00:09:10)

Recommend setting delta as the pager for specific git commands
This PR changes the way in which users are recommended to use delta: instead of recommending users to set `core.pager`, we recommend them to set delta as the pager for specific git commands (`diff`, `log`, `reflog`, `show`).

Does anyone have opinions on whether this is the right thing to do? cc @tpoliaw @th1000s @Kr1ss-XD @alerque) ref https://github.com/dandavison/delta/pull/547

The motivation for doing this is that setting `core.pager=delta` is instructing git to send all its output, from any command, even if it has nothing to do with diffs, to delta. But delta isn't a general-purpose pager, and indeed behaves very strangely if it is used as one, e.g.

```
~ echo 'hello\ncommit \nbye' | delta
hello
────────┐
commit  │
────────┘
bye
```

I don't yet have a realistic pathological example, but currently, if we have `core.pager=delta`, then delta is handling git's `help` output, and it definitely seems like `^commit ` could occur there.

Another example is that `delta` is handling `git grep` output which, again, it was never intended for.

alerque: This sounds much saner approach than the previous arrangement which was easy but circular and involved silent hardcoded fallbacks. More work for the user to setup but much more likely to do the right thing in the end.

Patch:
diff --git a/README.md b/README.md
index bda9f68..9ff487d 100644
--- a/README.md
+++ b/README.md
@@ -55,8 +55,11 @@ The most convenient way to configure delta is with a `[delta]` section in `~/.gi
 <sub>
 
 ```gitconfig
-[core]
-    pager = delta
+[pager]
+    diff = delta
+    log = delta
+    reflog = delta
+    show = delta
 
 [interactive]
     diffFilter = delta --color-only
@@ -278,13 +281,16 @@ Behind the scenes, delta uses `less` for paging. The version of `less` that come
 
 ## Configuration
 
-#### Git config files
+#### Git config file
 
-Set delta to be git's pager in your `.gitconfig`. Delta has many options to alter colors and other details of the output; `delta --help` shows them all. An example is
+Set delta to be the pager for git commands in your `.gitconfig`. Delta has many options to alter colors and other details of the output; `delta --help` shows them all. An example is
 
 ```gitconfig
-[core]
-    pager = delta
+[pager]
+    diff = delta
+    log = delta
+    reflog = delta
+    show = delta
 
 [delta]
     plus-style = "syntax #012800"
diff --git a/src/cli.rs b/src/cli.rs
index 40629bc..8a8c162 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -372,14 +372,23 @@ pub struct Opt {
     /// (overline), or the combination 'ul ol'.
     pub file_decoration_style: String,
 
-    /// Format string for file hyperlinks. The placeholders "{path}" and "{line}" will be replaced
-    /// by the absolute file path and the line number, respectively. The default value of this
-    /// option creates hyperlinks using standard file URLs; your operating system should open these
-    /// in the application registered for that file type. However, these do not make use of the
-    /// line number. In order for the link to open the file at the correct line number, you could
-    /// use a custom URL format such as "file-line://{path}:{line}" and register an application to
-    /// handle the custom "file-line" URL scheme by opening the file in your editor/IDE at the
-    /// indicated line number. See https://github.com/dandavison/open-in-editor for an example.
+    /// Format string for commit hyperlinks (requires --hyperlinks). The
+    /// placeholder "{commit}" will be replaced by the commit hash. For example:
+    /// --hyperlinks-commit-link-format='https://mygitrepo/{commit}/'
+    #[structopt(long = "hyperlinks-commit-link-format")]
+    pub hyperlinks_commit_link_format: Option<String>,
+
+    /// Format string for file hyperlinks (requires --hyperlinks). The
+    /// placeholders "{path}" and "{line}" will be replaced by the absolute file
+    /// path and the line number, respectively. The default value of this option
+    /// creates hyperlinks using standard file URLs; your operating system
+    /// should open these in the application registered for that file type.
+    /// However, these do not make use of the line number. In order for the link
+    /// to open the file at the correct line number, you could use a custom URL
+    /// format such as "file-line://{path}:{line}" and register an application
+    /// to handle the custom "file-line" URL scheme by opening the file in your
+    /// editor/IDE at the indicated line number. See
+    /// https://github.com/dandavison/open-in-editor for an example.
     #[structopt(long = "hyperlinks-file-link-format", default_value = "file://{path}")]
     pub hyperlinks_file_link_format: String,
 
diff --git a/src/config.rs b/src/config.rs
index 8d72fb3..0b8ab6d 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -41,6 +41,7 @@ pub struct Config {
     pub hunk_header_style_include_file_path: bool,
     pub hunk_header_style_include_line_number: bool,
     pub hyperlinks: bool,
+    pub hyperlinks_commit_link_format: Option<String>,
     pub hyperlinks_file_link_format: String,
     pub inspect_raw_lines: cli::InspectRawLines,
     pub keep_plus_minus_markers: bool,
@@ -220,6 +221,7 @@ impl From<cli::Opt> for Config {
                 .split(' ')
                 .any(|s| s == "line-number"),
             hyperlinks: opt.hyperlinks,
+            hyperlinks_commit_link_format: opt.hyperlinks_commit_link_format,
             hyperlinks_file_link_format: opt.hyperlinks_file_link_format,
             inspect_raw_lines: opt.computed.inspect_raw_lines,
             keep_plus_minus_markers: opt.keep_plus_minus_markers,
diff --git a/src/features/hyperlinks.rs b/src/features/hyperlinks.rs
index d59f5e9..bf0c235 100644
--- a/src/features/hyperlinks.rs
+++ b/src/features/hyperlinks.rs
@@ -22,7 +22,12 @@ pub fn format_commit_line_with_osc8_commit_hyperlink<'a>(
     line: &'a str,
     config: &Config,
 ) -> Cow<'a, str> {
-    if let Some(GitConfigEntry::GitRemote(GitRemoteRepo::GitHubRepo(repo))) =
+    if let Some(commit_link_format) = &config.hyperlinks_commit_link_format {
+        COMMIT_LINE_REGEX.replace(line, |captures: &Captures| {
+            let commit = captures.get(2).unwrap().as_str();
+            format_osc8_hyperlink(&commit_link_format.replace("{commit}", commit), commit)
+        })
+    } else if let Some(GitConfigEntry::GitRemote(GitRemoteRepo::GitHubRepo(repo))) =
         config.git_config_entries.get("remote.origin.url")
     {
         COMMIT_LINE_REGEX.replace(line, |captures: &Captures| {
@@ -51,18 +56,22 @@ pub fn format_osc8_file_hyperlink<'a>(
         } else {
             url = url.replace("{line}", "")
         };
-        Cow::from(format!(
-            "{osc}8;;{url}{st}{text}{osc}8;;{st}",
-            url = url,
-            text = text,
-            osc = "\x1b]",
-            st = "\x1b\\"
-        ))
+        Cow::from(format_osc8_hyperlink(&url, text))
     } else {
         Cow::from(relative_path)
     }
 }
 
+fn format_osc8_hyperlink(url: &str, text: &str) -> String {
+    format!(
+        "{osc}8;;{url}{st}{text}{osc}8;;{st}",
+        url = url,
+        text = text,
+        osc = "\x1b]",
+        st = "\x1b\\"
+    )
+}
+
 lazy_static! {
     static ref COMMIT_LINE_REGEX: Regex = Regex::new("(.* )([0-9a-f]{40})(.*)").unwrap();
 }
diff --git a/src/options/set.rs b/src/options/set.rs
index b27907e..a737fd4 100644
--- a/src/options/set.rs
+++ b/src/options/set.rs
@@ -142,6 +142,7 @@ pub fn set_options(
             hunk_header_line_number_style,
             hunk_header_style,
             hyperlinks,
+            hyperlinks_commit_link_format,
             hyperlinks_file_link_format,
             inspect_raw_lines,
             keep_plus_minus_markers,


Test patch:
diff --git a/src/tests/test_example_diffs.rs b/src/tests/test_example_diffs.rs
index 82b12f5..7c7192e 100644
--- a/src/tests/test_example_diffs.rs
+++ b/src/tests/test_example_diffs.rs
@@ -1591,6 +1591,23 @@ src/align.rs:71: impl<'a> Alignment<'a> { │
         assert!(output.contains(r"src/delta.rs: mode -x"));
     }
 
+    #[test]
+    fn test_hyperlinks_commit_link_format() {
+        let config = integration_test_utils::make_config_from_args(&[
+            // If commit-style is not set then the commit line is handled in raw
+            // mode, in which case we only format hyperlinks if output is a tty;
+            // this causes the test to fail on Github Actions, but pass locally
+            // if output is left going to the screen.
+            "--commit-style",
+            "blue",
+            "--hyperlinks",
+            "--hyperlinks-commit-link-format",
+            "https://invent.kde.org/utilities/konsole/-/commit/{commit}",
+        ]);
+        let output = integration_test_utils::run_delta(GIT_DIFF_SINGLE_HUNK, &config);
+        assert!(output.contains(r"https://invent.kde.org/utilities/konsole/-/commit/94907c0f136f46dc46ffae2dc92dca9af7eb7c2e"));
+    }
+
     const GIT_DIFF_SINGLE_HUNK: &str = "\
 commit 94907c0f136f46dc46ffae2dc92dca9af7eb7c2e
 Author: Dan Davison <dandavison7@gmail.com>