Repository: dandavison/delta
Pull request #550 (2021-03-28 01:36:36)

Provide an option to cycle through delta themes using --show-themes
When delta is invoked with `--show-themes`, cycles through all themes in the git config, where a theme is identified as a section with a "dark" or a "light" entry. Uses the computed `is_light_mode` value to determine whether to show light themes or dark themes by default.

When either the `--dark` or `--light` flag is passed in with `--show-themes`, will additionally display the dark or light themes, respectively. When `--light` and `--dark` are passed in, shows all themes.

The user can navigate from theme to theme using the `n` and `N` navigation keys.

In addition to viewing the themes using a sample diff, users can display themes on their own diff by piping it into delta, e.g. `git show | delta --show-themes`.

That being said, I might still swap out the sample commits used in the display, if I find something that seems to exhibit the themes' characteristics more clearly.

I'd also like to add a light theme or two to themes.gitconfig, because right now it only has dark themes to display. 


Patch:
diff --git a/Makefile b/Makefile
index b0d9684..c534338 100644
--- a/Makefile
+++ b/Makefile
@@ -2,7 +2,7 @@ build:
 	cargo build --release
 
 format:
-	git ls-files | grep '\.rs$' | xargs -P 0 rustfmt
+	rustfmt src/*.rs
 
 lint:
 	cargo clippy
diff --git a/README.md b/README.md
index 8b52908..738fb90 100644
--- a/README.md
+++ b/README.md
@@ -230,7 +230,7 @@ Alternatively, delta is available in the following package managers:
   <tr>
     <td>Debian / Ubuntu</td>
     <td><br>.deb files are on the <a href="https://github.com/dandavison/delta/releases">releases</a> page and at <a href="https://github.com/barnumbirr/delta-debian/releases">barnumbirr/delta-debian</a><br>
-    <code>dpkg -i file.deb</code>. **IMPORTANT** If you are using Ubuntu <= 19.10 or are mixing apt sources, read https://github.com/dandavison/delta/issues/504, be extremely cautious, and try the versions linked against musl.</td>
+    <code>dpkg -i file.deb</code></td>
   </tr>
   <tr>
     <td>Fedora</td>
diff --git a/src/bat_utils/output.rs b/src/bat_utils/output.rs
index 26cd4e2..388bc0c 100644
--- a/src/bat_utils/output.rs
+++ b/src/bat_utils/output.rs
@@ -137,6 +137,9 @@ delta is not an appropriate value for $PAGER \
                         navigate::copy_less_hist_file_and_append_navigate_regexp(config)
                     {
                         process.env("LESSHISTFILE", hist_file);
+                        if config.show_themes {
+                            process.arg("+n");
+                        }
                     }
                 }
                 Ok(process
diff --git a/src/cli.rs b/src/cli.rs
index 41f6eb8..2ee7324 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -1,5 +1,4 @@
 use std::collections::{HashMap, HashSet};
-#[cfg(test)]
 use std::ffi::OsString;
 use std::path::PathBuf;
 
@@ -266,10 +265,23 @@ pub struct Opt {
 
     /// Show all available syntax-highlighting themes, each with an example of highlighted diff output.
     /// If diff output is supplied on standard input then this will be used for the demo. For
-    /// example: `git show --color=always | delta --show-syntax-themes`.
+    /// example: `git show | delta --show-syntax-themes`.
     #[structopt(long = "show-syntax-themes")]
     pub show_syntax_themes: bool,
 
+    /// Show available delta themes, each with an example of highlighted diff
+    /// output. A delta theme is a delta named feature (see --features) that
+    /// sets either `light` or `dark`. See
+    /// https://github.com/dandavison/delta#custom-color-themes. If diff output
+    /// is supplied on standard input then this will be used for the demo. For
+    /// example: `git show | delta --show-themes`. By default shows dark or
+    /// light themes only, according to whether delta is in dark or light mode
+    /// (as set by the user or inferred from BAT_THEME). To control the themes
+    /// shown, use --dark or --light, or both, on the command line together with
+    /// this option.
+    #[structopt(long = "show-themes")]
+    pub show_themes: bool,
+
     #[structopt(long = "no-gitconfig")]
     /// Do not take any settings from git config. See GIT CONFIG section.
     pub no_gitconfig: bool,
@@ -635,7 +647,6 @@ impl Opt {
         Self::from_clap_and_git_config(Self::clap().get_matches(), git_config, assets)
     }
 
-    #[cfg(test)]
     pub fn from_iter_and_git_config<I>(iter: I, git_config: &mut Option<GitConfig>) -> Self
     where
         I: IntoIterator,
diff --git a/src/config.rs b/src/config.rs
index 23b7b97..f822efc 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -13,6 +13,7 @@ use crate::cli;
 use crate::color;
 use crate::delta::State;
 use crate::env;
+use crate::features::navigate;
 use crate::features::side_by_side;
 use crate::git_config::GitConfigEntry;
 use crate::style::{self, Style};
@@ -58,6 +59,7 @@ pub struct Config {
     pub minus_non_emph_style: Style,
     pub minus_style: Style,
     pub navigate: bool,
+    pub navigate_regexp: Option<String>,
     pub null_style: Style,
     pub null_syntect_style: SyntectStyle,
     pub paging_mode: PagingMode,
@@ -68,6 +70,7 @@ pub struct Config {
     pub plus_style: Style,
     pub git_minus_style: Style,
     pub git_plus_style: Style,
+    pub show_themes: bool,
     pub side_by_side: bool,
     pub side_by_side_data: side_by_side::SideBySideData,
     pub syntax_dummy_theme: SyntaxTheme,
@@ -154,6 +157,24 @@ impl From<cli::Opt> for Config {
             _ => *style::GIT_DEFAULT_PLUS_STYLE,
         };
 
+        let file_added_label = opt.file_added_label;
+        let file_copied_label = opt.file_copied_label;
+        let file_modified_label = opt.file_modified_label;
+        let file_removed_label = opt.file_removed_label;
+        let file_renamed_label = opt.file_renamed_label;
+
+        let navigate_regexp = if opt.navigate || opt.show_themes {
+            Some(navigate::make_navigate_regexp(
+                opt.show_themes,
+                &file_modified_label,
+                &file_added_label,
+                &file_removed_label,
+                &file_renamed_label,
+            ))
+        } else {
+            None
+        };
+
         Self {
             available_terminal_width: opt.computed.available_terminal_width,
             background_color_extends_to_terminal_width: opt
@@ -163,11 +184,11 @@ impl From<cli::Opt> for Config {
             color_only: opt.color_only,
             decorations_width: opt.computed.decorations_width,
             error_exit_code: 2, // Use 2 for error because diff uses 0 and 1 for non-error.
-            file_added_label: opt.file_added_label,
-            file_copied_label: opt.file_copied_label,
-            file_modified_label: opt.file_modified_label,
-            file_removed_label: opt.file_removed_label,
-            file_renamed_label: opt.file_renamed_label,
+            file_added_label,
+            file_copied_label,
+            file_modified_label,
+            file_removed_label,
+            file_renamed_label,
             file_style,
             git_config_entries: opt.git_config_entries,
             hunk_header_file_style,
@@ -203,6 +224,7 @@ impl From<cli::Opt> for Config {
             minus_non_emph_style,
             minus_style,
             navigate: opt.navigate,
+            navigate_regexp,
             null_style: Style::new(),
             null_syntect_style: SyntectStyle::default(),
             paging_mode: opt.computed.paging_mode,
@@ -213,6 +235,7 @@ impl From<cli::Opt> for Config {
             plus_style,
             git_minus_style,
             git_plus_style,
+            show_themes: opt.show_themes,
             side_by_side: opt.side_by_side,
             side_by_side_data,
             syntax_dummy_theme: SyntaxTheme::default(),
diff --git a/src/features/navigate.rs b/src/features/navigate.rs
index a38815a..0020d54 100644
--- a/src/features/navigate.rs
+++ b/src/features/navigate.rs
@@ -23,14 +23,22 @@ pub fn make_feature() -> Vec<(String, OptionValueFunction)> {
     ])
 }
 
-fn make_navigate_regexp(config: &Config) -> String {
-    format!(
-        "^(commit|{}|{}|{}|{})",
-        config.file_modified_label,
-        config.file_added_label,
-        config.file_removed_label,
-        config.file_renamed_label
-    )
+// Construct the regexp used by less for paging, if --show-themes or --navigate is enabled.
+pub fn make_navigate_regexp(
+    show_themes: bool,
+    file_modified_label: &str,
+    file_added_label: &str,
+    file_removed_label: &str,
+    file_renamed_label: &str,
+) -> String {
+    if show_themes {
+        "^Theme:".to_string()
+    } else {
+        format!(
+            "^(commit|{}|{}|{}|{})",
+            file_modified_label, file_added_label, file_removed_label, file_renamed_label,
+        )
+    }
 }
 
 // Create a less history file to be used by delta's child less process. This file is initialized
@@ -57,7 +65,7 @@ pub fn copy_less_hist_file_and_append_navigate_regexp(config: &Config) -> std::i
         std::fs::File::create(&delta_less_hist_file)?,
         "{}\"{}",
         contents,
-        make_navigate_regexp(config)
+        config.navigate_regexp.as_ref().unwrap(),
     )?;
     Ok(delta_less_hist_file)
 }
diff --git a/src/git_config/mod.rs b/src/git_config/mod.rs
index 827446e..13b494b 100644
--- a/src/git_config/mod.rs
+++ b/src/git_config/mod.rs
@@ -12,7 +12,7 @@ use std::process;
 use lazy_static::lazy_static;
 
 pub struct GitConfig {
-    config: git2::Config,
+    pub config: git2::Config,
     config_from_env_var: HashMap<String, String>,
     pub enabled: bool,
     pub repo: Option<git2::Repository>,
diff --git a/src/main.rs b/src/main.rs
index 5757fba..c99fb46 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -22,6 +22,7 @@ mod options;
 mod paint;
 mod parse;
 mod parse_style;
+mod sample_diff;
 mod style;
 mod syntect_color;
 mod tests;
@@ -38,6 +39,7 @@ use crate::bat_utils::assets::{list_languages, HighlightingAssets};
 use crate::bat_utils::output::{OutputType, PagingMode};
 use crate::config::delta_unreachable;
 use crate::delta::delta;
+use crate::options::get::get_themes;
 use crate::options::theme::is_light_syntax_theme;
 
 pub mod errors {
@@ -65,6 +67,9 @@ fn run_app() -> std::io::Result<i32> {
     } else if opt.show_syntax_themes {
         show_syntax_themes()?;
         return Ok(0);
+    } else if opt.show_themes {
+        show_themes(opt.dark, opt.light, opt.computed.is_light_mode)?;
+        return Ok(0);
     }
 
     let _show_config = opt.show_config;
@@ -254,6 +259,7 @@ fn show_config(config: &config::Config, writer: &mut dyn Write) -> std::io::Resu
         "    max-line-distance             = {max_line_distance}
     max-line-length               = {max_line_length}
     navigate                      = {navigate}
+    navigate-regexp               = {navigate_regexp}
     paging                        = {paging_mode}
     side-by-side                  = {side_by_side}
     syntax-theme                  = {syntax_theme}
@@ -263,6 +269,10 @@ fn show_config(config: &config::Config, writer: &mut dyn Write) -> std::io::Resu
         max_line_distance = config.max_line_distance,
         max_line_length = config.max_line_length,
         navigate = config.navigate,
+        navigate_regexp = match &config.navigate_regexp {
+            None => "".to_string(),
+            Some(s) => s.to_string(),
+        },
         paging_mode = match config.paging_mode {
             PagingMode::Always => "always",
             PagingMode::Never => "never",
@@ -302,6 +312,56 @@ where
     }
 }
 
+fn show_themes(dark: bool, light: bool, computed_theme_is_light: bool) -> std::io::Result<()> {
+    use bytelines::ByteLines;
+    use sample_diff::DIFF;
+    use std::io::BufReader;
+    let mut input = DIFF.to_vec();
+
+    if !atty::is(atty::Stream::Stdin) {
+        let mut buf = Vec::new();
+        io::stdin().lock().read_to_end(&mut buf)?;
+        if !buf.is_empty() {
+            input = buf;
+        }
+    };
+
+    let mut git_config = git_config::GitConfig::try_create();
+    let opt = cli::Opt::from_iter_and_git_config(
+        &["", "", "--navigate", "--show-themes"],
+        &mut git_config,
+    );
+    let mut output_type =
+        OutputType::from_mode(PagingMode::Always, None, &config::Config::from(opt)).unwrap();
+    let title_style = ansi_term::Style::new().bold();
+    let writer = output_type.handle().unwrap();
+
+    for theme in &get_themes(git_config::GitConfig::try_create()) {
+        let opt =
+            cli::Opt::from_iter_and_git_config(&["", "", "--features", &theme], &mut git_config);
+        let is_dark_theme = opt.dark;
+        let is_light_theme = opt.light;
+        let config = config::Config::from(opt);
+
+        if (!computed_theme_is_light && is_dark_theme)
+            || (computed_theme_is_light && is_light_theme)
+            || (dark && light)
+        {
+            writeln!(writer, "\n\nTheme: {}\n", title_style.paint(theme))?;
+
+            if let Err(error) = delta(ByteLines::new(BufReader::new(&input[0..])), writer, &config)
+            {
+                match error.kind() {
+                    ErrorKind::BrokenPipe => process::exit(0),
+                    _ => eprintln!("{}", error),
+                }
+            }
+        }
+    }
+
+    Ok(())
+}
+
 #[cfg(not(tarpaulin_include))]
 fn show_syntax_themes() -> std::io::Result<()> {
     let mut opt = cli::Opt::from_args();
diff --git a/src/options/get.rs b/src/options/get.rs
index a36987c..b907ee8 100644
--- a/src/options/get.rs
+++ b/src/options/get.rs
@@ -1,3 +1,5 @@
+use lazy_static::lazy_static;
+use regex::Regex;
 use std::collections::HashMap;
 
 use crate::cli;
@@ -38,6 +40,27 @@ where
     T::get_option_value(option_name, builtin_features, opt, git_config)
 }
 
+lazy_static! {
+    static ref GIT_CONFIG_THEME_REGEX: Regex = Regex::new(r"^delta\.(.+)\.(light|dark)$").unwrap();
+}
+
+pub fn get_themes(git_config: Option<git_config::GitConfig>) -> Vec<String> {
+    let mut themes: Vec<String> = Vec::new();
+    for e in &git_config.unwrap().config.entries(None).unwrap() {
+        let entry = e.unwrap();
+        let entry_name = entry.name().unwrap();
+        let caps = GIT_CONFIG_THEME_REGEX.captures(entry_name);
+        if let Some(caps) = caps {
+            let name = caps.get(1).map_or("", |m| m.as_str()).to_string();
+            if !themes.contains(&name) {
+                themes.push(name)
+            }
+        }
+    }
+    themes.sort_by(|a, b| a.to_lowercase().cmp(&b.to_lowercase()));
+    themes
+}
+
 pub trait GetOptionValue {
     fn get_option_value(
         option_name: &str,
@@ -115,6 +138,7 @@ pub mod tests {
     use std::env;
     use std::fs::remove_file;
 
+    use crate::options::get::get_themes;
     use crate::tests::integration_test_utils::integration_test_utils;
 
     // TODO: the followig tests are collapsed into one since they all set the same env var and thus
@@ -275,4 +299,41 @@ pub mod tests {
 
         remove_file(git_config_path).unwrap();
     }
+
+    #[test]
+    fn test_get_themes_from_config() {
+        let git_config_contents = r#"
+[delta "dark-theme"]
+    max-line-distance = 0.6
+    dark = true
+
+[delta "light-theme"]
+    max-line-distance = 0.6
+    light = true
+
+[delta "light-and-dark-theme"]
+    max-line-distance = 0.6
+    light = true
+    dark = true
+
+[delta "not-a-theme"]
+    max-line-distance = 0.6
+"#;
+        let git_config_path = "delta__test_get_themes_git_config.gitconfig";
+
+        let git_config = Some(integration_test_utils::make_git_config(
+            git_config_contents.as_bytes(),
+            git_config_path,
+            false,
+        ));
+
+        let themes = get_themes(git_config);
+
+        assert_eq!(
+            themes,
+            ["dark-theme", "light-and-dark-theme", "light-theme",]
+        );
+
+        remove_file(git_config_path).unwrap();
+    }
 }
diff --git a/src/options/set.rs b/src/options/set.rs
index 354eeea..e5c1edf 100644
--- a/src/options/set.rs
+++ b/src/options/set.rs
@@ -169,6 +169,7 @@ pub fn set_options(
             plus_empty_line_marker_style,
             plus_non_emph_style,
             raw,
+            show_themes,
             side_by_side,
             tab_width,
             tokenization_regex,
diff --git a/src/options/theme.rs b/src/options/theme.rs
index ae83120..420c5be 100644
--- a/src/options/theme.rs
+++ b/src/options/theme.rs
@@ -66,7 +66,7 @@ fn is_no_syntax_highlighting_syntax_theme_name(theme_name: &str) -> bool {
 ///    chosen for a light or dark terminal background. (`bat` has no equivalent.)
 ///
 /// Basically:
-/// 1. The theme is specified by the `--theme` option. If this isn't supplied then it is specified
+/// 1. The theme is specified by the `--syntax-theme` option. If this isn't supplied then it is specified
 ///    by the `BAT_THEME` environment variable.
 /// 2. Light vs dark mode is specified by the `--light` or `--dark` options. If these aren't
 ///    supplied then it is inferred from the chosen theme.
diff --git a/src/sample_diff.rs b/src/sample_diff.rs
new file mode 100644
index 0000000..90de426
--- /dev/null
+++ b/src/sample_diff.rs
@@ -0,0 +1,93 @@
+pub const DIFF: &[u8] = r#"
+commit 7ec4627902020cccd7b3f4fbc63e1b0d6b9798cd
+Author: Evan You <yyx990803@gmail.com>
+Date:   Thu Feb 21 08:52:15 2019 -0500
+
+    fix: ensure generated scoped slot code is compatible with 2.5
+    
+    fix #9545
+
+diff --git a/src/compiler/codegen/index.js b/src/compiler/codegen/index.js
+index a64c3421..d433f756 100644
+--- a/src/compiler/codegen/index.js
++++ b/src/compiler/codegen/index.js
+@@ -409,9 +409,9 @@ function genScopedSlots (
+     .join(',')
+ 
+   return `scopedSlots:_u([${generatedSlots}]${
+-    needsForceUpdate ? `,true` : ``
++    needsForceUpdate ? `,null,true` : ``
+   }${
+-    !needsForceUpdate && needsKey ? `,false,${hash(generatedSlots)}` : ``
++    !needsForceUpdate && needsKey ? `,null,false,${hash(generatedSlots)}` : ``
+   })`
+ }
+ 
+diff --git a/src/core/instance/render-helpers/resolve-scoped-slots.js b/src/core/instance/render-helpers/resolve-scoped-slots.js
+index 6439324b..f11ca000 100644
+--- a/src/core/instance/render-helpers/resolve-scoped-slots.js
++++ b/src/core/instance/render-helpers/resolve-scoped-slots.js
+@@ -2,15 +2,16 @@
+ 
+ export function resolveScopedSlots (
+   fns: ScopedSlotsData, // see flow/vnode
+-  hasDynamicKeys: boolean,
+-  contentHashKey: number,
+-  res?: Object
++  res?: Object,
++  // the following are added in 2.6
++  hasDynamicKeys?: boolean,
++  contentHashKey?: number
+ ): { [key: string]: Function, $stable: boolean } {
+   res = res || { $stable: !hasDynamicKeys }
+   for (let i = 0; i < fns.length; i++) {
+     const slot = fns[i]
+     if (Array.isArray(slot)) {
+-      resolveScopedSlots(slot, hasDynamicKeys, null, res)
++      resolveScopedSlots(slot, res, hasDynamicKeys)
+     } else if (slot) {
+       // marker for reverse proxying v-slot without scope on this.$slots
+       if (slot.proxy) {
+@@ -20,7 +21,7 @@ export function resolveScopedSlots (
+     }
+   }
+   if (contentHashKey) {
+-    res.$key = contentHashKey
++    (res: any).$key = contentHashKey
+   }
+   return res
+ }
+diff --git a/test/unit/modules/compiler/codegen.spec.js b/test/unit/modules/compiler/codegen.spec.js
+index 98c202dd..e56b2576 100644
+--- a/test/unit/modules/compiler/codegen.spec.js
++++ b/test/unit/modules/compiler/codegen.spec.js
+@@ -232,25 +232,25 @@ describe('codegen', () => {
+   it('generate dynamic scoped slot', () => {
+     assertCodegen(
+       '<foo><template :slot="foo" slot-scope="bar">{{ bar }}</template></foo>',
+-      `with(this){return _c('foo',{scopedSlots:_u([{key:foo,fn:function(bar){return [_v(_s(bar))]}}],true)})}`
++      `with(this){return _c('foo',{scopedSlots:_u([{key:foo,fn:function(bar){return [_v(_s(bar))]}}],null,true)})}`
+     )
+   })
+ 
+   it('generate scoped slot with multiline v-if', () => {
+     assertCodegen(
+       '<foo><template v-if="\nshow\n" slot-scope="bar">{{ bar }}</template></foo>',
+-      `with(this){return _c('foo',{scopedSlots:_u([{key:"default",fn:function(bar){return (\nshow\n)?[_v(_s(bar))]:undefined}}],true)})}`
++      `with(this){return _c('foo',{scopedSlots:_u([{key:"default",fn:function(bar){return (\nshow\n)?[_v(_s(bar))]:undefined}}],null,true)})}`
+     )
+     assertCodegen(
+       '<foo><div v-if="\nshow\n" slot="foo" slot-scope="bar">{{ bar }}</div></foo>',
+-      `with(this){return _c(\'foo\',{scopedSlots:_u([{key:"foo",fn:function(bar){return (\nshow\n)?_c(\'div\',{},[_v(_s(bar))]):_e()}}],true)})}`
++      `with(this){return _c(\'foo\',{scopedSlots:_u([{key:"foo",fn:function(bar){return (\nshow\n)?_c(\'div\',{},[_v(_s(bar))]):_e()}}],null,true)})}`
+     )
+   })
+ 
+   it('generate scoped slot with new slot syntax', () => {
+     assertCodegen(
+       '<foo><template v-if="show" #default="bar">{{ bar }}</template></foo>',
+-      `with(this){return _c('foo',{scopedSlots:_u([(show)?{key:"default",fn:function(bar){return [_v(_s(bar))]}}:null],true)})}`
++      `with(this){return _c('foo',{scopedSlots:_u([(show)?{key:"default",fn:function(bar){return [_v(_s(bar))]}}:null],null,true)})}`
+     )
+   })
+ "#.as_bytes();
diff --git a/themes.gitconfig b/themes.gitconfig
index 6818a24..c549a3a 100644
--- a/themes.gitconfig
+++ b/themes.gitconfig
@@ -14,8 +14,9 @@
 #    organism: mammal, bird, plant, mollusk -- whatever. It can be in
 #    any language.
 #
-# 2. Please include a comment line describing the terminal
-#    theme/colors that you typically use with this theme.
+# 2. Include either `dark = true` or `light = true` according to whether you feel
+#    it looks best on a dark or light terminal background. (This marks a feature
+#    as a "theme", causing it to be picked up by `delta --show-themes`).
 #
 # 3. Feel free to include a comment line indicating who is the author
 #    of the theme. E.g. a link to your github user page.
@@ -52,6 +53,7 @@
 
 [delta "villsau"]
     # author: https://github.com/torarnv
+    dark = true
     syntax-theme = OneHalfDark
     line-numbers = false
     hunk-header-style = file line-number syntax
@@ -73,6 +75,7 @@
 [delta "collared-trogon"]
     # author: https://github.com/clnoll
     commit-decoration-style = bold box ul
+    dark = true
     file-style = omit
     file-decoration-style = none
     hunk-header-style = file line-number syntax
@@ -94,6 +97,7 @@
 [delta "tangara-chilensis"]
     # author: https://github.com/clnoll
     commit-decoration-style = bold box ul "#34fd50"
+    dark = true
     file-style = omit
     file-decoration-style = none
     hunk-header-style = file line-number syntax
@@ -112,28 +116,3 @@
     plus-emph-style = syntax "#03a4ff"
     side-by-side = true
     syntax-theme = Vibrant Sunburst
-
-[delta "coracias-caudatus"]
-    # author: https://github.com/clnoll
-    commit-decoration-style = ol "#7536ff"
-    commit-style = "#200078"
-    file-decoration-style = none
-    file-style = omit
-    hunk-header-decoration-style = "#cfd6ff" ul
-    hunk-header-file-style = "#858dff"
-    hunk-header-line-number-style = "#7536ff"
-    hunk-header-style = file line-number syntax
-    light = true
-    line-numbers = true
-    line-numbers-left-format = "{nm:>4} ."
-    line-numbers-left-style = "#e3ab02"
-    line-numbers-minus-style = "#ff38b6"
-    line-numbers-plus-style = "#00e0c2"
-    line-numbers-right-format = "{np:>4}  "
-    line-numbers-right-style = white
-    line-numbers-zero-style = "#cccccc"
-    minus-emph-style = bold "#ff3838" "#ffe3f7"
-    minus-style = "#ff0080"
-    plus-emph-style = "#008a81" bold "#00ffbf"
-    plus-style = syntax "#cffff3"
-    syntax-theme = Github


Test patch:
diff --git a/src/tests/integration_test_utils.rs b/src/tests/integration_test_utils.rs
index 8eb3674..1fdb215 100644
--- a/src/tests/integration_test_utils.rs
+++ b/src/tests/integration_test_utils.rs
@@ -69,7 +69,7 @@ pub mod integration_test_utils {
         config::Config::from(make_options_from_args(args))
     }
 
-    fn make_git_config(contents: &[u8], path: &str, honor_env_var: bool) -> GitConfig {
+    pub fn make_git_config(contents: &[u8], path: &str, honor_env_var: bool) -> GitConfig {
         let path = Path::new(path);
         let mut file = File::create(path).unwrap();
         file.write_all(contents).unwrap();