diff --git a/helix-term/src/application.rs b/helix-term/src/application.rs
index 6e66041b..d26f6218 100644
--- a/helix-term/src/application.rs
+++ b/helix-term/src/application.rs
@@ -13,7 +13,8 @@ use helix_stdx::path::get_relative_path;
 use helix_view::{
     align_view,
     document::{DocumentOpenError, DocumentSavedEventResult},
-    editor::{ConfigEvent, EditorEvent},
+    editor::{ConfigEvent, EditorEvent, TICK_DURATION},
+    events::DidRequestInlineBlame,
     graphics::Rect,
     theme,
     tree::Layout,
@@ -35,7 +36,7 @@ use crate::{
 use log::{debug, error, info, warn};
 #[cfg(not(feature = "integration"))]
 use std::io::stdout;
-use std::{io::stdin, path::Path, sync::Arc};
+use std::{io::stdin, path::Path, sync::Arc, time::Duration};
 
 #[cfg(not(windows))]
 use anyhow::Context;
@@ -586,6 +587,18 @@ impl Application {
         ));
     }
 
+    async fn handle_tick(&mut self) {
+        self.editor.ticks_elapsed += 1;
+        // request an inline blame refresh every this amount of ticks
+        const INLINE_BLAME_REQUEST_TIME: u128 =
+            Duration::from_millis(150).as_nanos() / TICK_DURATION.as_nanos();
+        if self.editor.ticks_elapsed % INLINE_BLAME_REQUEST_TIME == 0 {
+            helix_event::dispatch(DidRequestInlineBlame {
+                editor: &mut self.editor,
+            })
+        };
+    }
+
     #[inline(always)]
     pub async fn handle_editor_event(&mut self, event: EditorEvent) -> bool {
         log::debug!("received editor event: {:?}", event);
@@ -622,6 +635,9 @@ impl Application {
                     return true;
                 }
             }
+            EditorEvent::Tick => {
+                self.handle_tick().await;
+            }
         }
 
         false
diff --git a/helix-term/src/events.rs b/helix-term/src/events.rs
index 9d06627c..210dde57 100644
--- a/helix-term/src/events.rs
+++ b/helix-term/src/events.rs
@@ -1,7 +1,7 @@
 use helix_event::{events, register_event};
 use helix_view::document::Mode;
 use helix_view::events::{
-    DiagnosticsDidChange, DocumentDidChange, DocumentDidOpen, DocumentFocusLost, SelectionDidChange,
+    DiagnosticsDidChange, DocumentDidChange, DocumentDidOpen, DocumentFocusLost, SelectionDidChange, DidRequestInlineBlame
 };
 
 use crate::commands;
@@ -20,6 +20,7 @@ pub fn register() {
     register_event::<DocumentDidChange>();
     register_event::<DocumentDidOpen>();
     register_event::<DocumentFocusLost>();
+    register_event::<DidRequestInlineBlame>();
     register_event::<SelectionDidChange>();
     register_event::<DiagnosticsDidChange>();
 }
diff --git a/helix-term/src/handlers/blame.rs b/helix-term/src/handlers/blame.rs
index 923ae74c..12cb58b2 100644
--- a/helix-term/src/handlers/blame.rs
+++ b/helix-term/src/handlers/blame.rs
@@ -1,10 +1,13 @@
 use std::time::Duration;
 
 use helix_event::{register_hook, send_blocking};
-use helix_view::handlers::{BlameEvent, Handlers};
+use helix_view::{
+    events::DidRequestInlineBlame,
+    handlers::{BlameEvent, Handlers},
+};
 use tokio::{task::JoinHandle, time::Instant};
 
-use crate::{events::PostCommand, job};
+use crate::job;
 
 #[derive(Default)]
 pub struct BlameHandler {
@@ -77,13 +80,14 @@ impl helix_event::AsyncHook for BlameHandler {
 
 pub(super) fn register_hooks(handlers: &Handlers) {
     let tx = handlers.blame.clone();
-    register_hook!(move |event: &mut PostCommand<'_, '_>| {
-        let version_control_config = &event.cx.editor.config().version_control;
+    register_hook!(move |event: &mut DidRequestInlineBlame<'_>| {
+        let version_control_config = &event.editor.config().version_control;
         if !version_control_config.inline_blame {
             return Ok(());
         }
 
-        let (view, doc) = current!(event.cx.editor);
+        let (view, doc) = current!(event.editor);
+
         let Some(file) = doc.path() else {
             return Ok(());
         };
@@ -93,10 +97,24 @@ pub(super) fn register_hooks(handlers: &Handlers) {
             return Ok(());
         };
 
-        let hunks = doc.diff_handle().unwrap().load();
+        if let Some(cached) = &mut event.editor.blame_cache {
+            // don't update the blame if we haven't moved to a different line
+            if (view.id, cursor_line) == *cached {
+                return Ok(());
+            } else {
+                *cached = (view.id, cursor_line)
+            }
+        };
 
-        let (inserted_lines_count, deleted_lines_count) =
-            hunks.inserted_and_deleted_before_line(cursor_line as usize);
+        let Some(hunks) = doc.diff_handle() else {
+            return Ok(());
+        };
+
+        log::error!("updated blame!");
+
+        let (inserted_lines_count, deleted_lines_count) = hunks
+            .load()
+            .inserted_and_deleted_before_line(cursor_line as usize);
 
         send_blocking(
             &tx,
@@ -106,7 +124,7 @@ pub(super) fn register_hooks(handlers: &Handlers) {
                 deleted_lines_count,
                 inserted_lines_count,
                 // ok to clone because diff_providers is very small
-                diff_providers: event.cx.editor.diff_providers.clone(),
+                diff_providers: event.editor.diff_providers.clone(),
                 // ok to clone because blame_format is likely to be about 30 characters or less
                 blame_format: version_control_config.inline_blame_format.clone(),
             },
diff --git a/helix-vcs/src/git/blame.rs b/helix-vcs/src/git/blame.rs
index dbf44679..a7456c28 100644
--- a/helix-vcs/src/git/blame.rs
+++ b/helix-vcs/src/git/blame.rs
@@ -185,12 +185,10 @@ pub fn blame_line(
     })
 }
 
-// attributes on expressions are not allowed
-// however, in our macro its possible that sometimes the
-// assignment to the mutable variable will not be read.
-//
-// when the last line has no expected blame commit
-#[allow(unused_assignments)]
+// For some reasons the CI is failing on windows with the message "Commits not found".
+// There is nothing windows-specific in this implementation
+// As long as these tests pass on other platforms, on Windows it should work too
+// #[cfg(not(windows))]
 #[cfg(test)]
 mod test {
     use std::fs::File;
@@ -341,6 +339,7 @@ mod test {
 
                 $(
                     let line_diff_flag = line_diff_flag!($($line_diff)?, $commit_msg, $line);
+                    #[allow(unused_assignments)]
                     match line_diff_flag {
                         LineDiff::Insert => added_lines += 1,
                         LineDiff::Delete => removed_lines += 1,
@@ -373,7 +372,10 @@ mod test {
                                     .unwrap_or("<no commit>")
                             );
                         )?
-                        line_number += 1;
+                        #[allow(unused_assignments)]
+                        {
+                            line_number += 1;
+                        }
                     }
                 )*
             )*
@@ -448,7 +450,7 @@ mod test {
                 "  four" delete,
                 "  two" delete,
                 "  five" delete,
-                "  four",
+                "  four" 5,
                 "}" 1,
                 "  five" 5,
                 "  four" 5;
diff --git a/helix-view/src/editor.rs b/helix-view/src/editor.rs
index 1a585ef0..81b968bc 100644
--- a/helix-view/src/editor.rs
+++ b/helix-view/src/editor.rs
@@ -23,7 +23,7 @@ use icons::Icons;
 use futures_util::stream::select_all::SelectAll;
 use futures_util::{future, StreamExt};
 use helix_lsp::{Call, LanguageServerId};
-use tokio_stream::wrappers::UnboundedReceiverStream;
+use tokio_stream::wrappers::{IntervalStream, UnboundedReceiverStream};
 
 use std::{
     borrow::Cow,
@@ -39,7 +39,7 @@ use std::{
 
 use tokio::{
     sync::mpsc::{unbounded_channel, UnboundedReceiver, UnboundedSender},
-    time::{sleep, Duration, Instant, Sleep},
+    time::{interval, sleep, Duration, Instant, Sleep},
 };
 
 use anyhow::{anyhow, bail, Error};
@@ -1111,6 +1111,8 @@ pub struct Editor {
     pub auto_pairs: Option<AutoPairs>,
 
     pub idle_timer: Pin<Box<Sleep>>,
+    pub tick: IntervalStream,
+    pub ticks_elapsed: u128,
     redraw_timer: Pin<Box<Sleep>>,
     last_motion: Option<Motion>,
     pub last_completion: Option<CompleteAction>,
@@ -1136,6 +1138,7 @@ pub struct Editor {
 
     pub mouse_down_range: Option<Range>,
     pub cursor_cache: CursorCache,
+    pub blame_cache: Option<(ViewId, u32)>,
 }
 
 pub type Motion = Box<dyn Fn(&mut Editor)>;
@@ -1147,6 +1150,7 @@ pub enum EditorEvent {
     LanguageServerMessage((LanguageServerId, Call)),
     DebuggerEvent(dap::Payload),
     IdleTimer,
+    Tick,
     Redraw,
 }
 
@@ -1201,6 +1205,9 @@ pub enum CloseError {
     SaveError(anyhow::Error),
 }
 
+/// A tick happens every this amount of milliseconds
+pub const TICK_DURATION: Duration = Duration::from_millis(1000 / 60);
+
 impl Editor {
     pub fn new(
         mut area: Rect,
@@ -1246,6 +1253,8 @@ impl Editor {
             status_msg: None,
             autoinfo: None,
             idle_timer: Box::pin(sleep(conf.idle_timeout)),
+            tick: IntervalStream::new(interval(TICK_DURATION)),
+            ticks_elapsed: 0,
             redraw_timer: Box::pin(sleep(Duration::MAX)),
             last_motion: None,
             last_completion: None,
@@ -1258,6 +1267,7 @@ impl Editor {
             handlers,
             mouse_down_range: None,
             cursor_cache: CursorCache::default(),
+            blame_cache: None,
         }
     }
 
@@ -2164,6 +2174,9 @@ impl Editor {
                 _ = &mut self.idle_timer  => {
                     return EditorEvent::IdleTimer
                 }
+                _ = &mut self.tick.next() => {
+                    return EditorEvent::Tick
+                }
             }
         }
     }
diff --git a/helix-view/src/events.rs b/helix-view/src/events.rs
index cb3ed90c..e3ea1304 100644
--- a/helix-view/src/events.rs
+++ b/helix-view/src/events.rs
@@ -16,4 +16,5 @@ events! {
     DiagnosticsDidChange<'a> { editor: &'a mut Editor, doc: DocumentId }
     // called **after** a document loses focus (but not when its closed)
     DocumentFocusLost<'a> { editor: &'a mut Editor, doc: DocumentId }
+    DidRequestInlineBlame<'a> { editor: &'a mut Editor }
 }