<?xml version="1.0" encoding="UTF-8"?>

<rss version="2.0"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:dc="http://purl.org/dc/elements/1.1/"
     xmlns:wfw="http://wellformedweb.org/CommentAPI/"
     >
  <channel>
    <atom:link href="http://kitchingroup.cheme.cmu.edu/blog/feed/index.xml" rel="self" type="application/rss+xml" />
    <title>The Kitchin Research Group</title>
    <link>https://kitchingroup.cheme.cmu.edu/blog</link>
    <description>Chemical Engineering at Carnegie Mellon University</description>
    <pubDate>Sat, 01 Nov 2025 13:47:46 GMT</pubDate>
    <generator>Blogofile</generator>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    
    <item>
      <title>A git status Emacs modeline</title>
      <link>https://kitchingroup.cheme.cmu.edu/blog/2014/09/19/A-git-status-Emacs-modeline</link>
      <pubDate>Fri, 19 Sep 2014 09:36:21 EDT</pubDate>
      <category><![CDATA[emacs]]></category>
      <category><![CDATA[git]]></category>
      <guid isPermaLink="false">YrJ_PS59YtLxhRMSSv_j0REI7SU=</guid>
      <description>A git status Emacs modeline</description>
      <content:encoded><![CDATA[


&lt;p&gt;
I am using git more and more in Emacs, and I would like a way to know the status of the git repo I am working in by looking at the modeline. I know about &lt;a href="https://github.com/magit/magit"&gt;magit&lt;/a&gt; , and other git modes, but none of them provide something as easy as useful as say &lt;a href="https://github.com/magicmonty/bash-git-prompt"&gt;bash-git-prompt&lt;/a&gt; in the bash shell, which is to say I do not want to run a command to see the status (I might as well be in the shell then). Part of this need comes from a project with hundreds of git repos in it, and I want convenient status when I open any one of them.
&lt;/p&gt;

&lt;p&gt;
Here, I want to emulate the bash-git-prompt feature in the Emacs modeline where it will show you when you are in a git repo, and then some basic information like what branch you are on, the number of untracked, modified files, and the commit status with respect to a remote. First, we only want this when we are in a git repo. We can check for that like this. The command in this block returns a string that starts with fatal when not in a git repo.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-emacs-lisp"&gt;(not (string-match &lt;span style="color: #228b22;"&gt;"^fatal"&lt;/span&gt; (shell-command-to-string &lt;span style="color: #228b22;"&gt;"git rev-parse --git-dir"&lt;/span&gt;)))
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
t
&lt;/pre&gt;

&lt;p&gt;
Let us wrap that in a nice function so we can use it later..
&lt;/p&gt;
&lt;div class="org-src-container"&gt;

&lt;pre class="src src-emacs-lisp"&gt;(&lt;span style="color: #8b0000;"&gt;defun&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;in-git-p&lt;/span&gt; ()
  (not (string-match &lt;span style="color: #228b22;"&gt;"^fatal"&lt;/span&gt; (shell-command-to-string &lt;span style="color: #228b22;"&gt;"git rev-parse --git-dir"&lt;/span&gt;))))

(in-git-p)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
t
&lt;/pre&gt;

&lt;p&gt;
Next, we would like to know how many untracked, modified and other (e.g. unmerged, deleted, etc&amp;#x2026;) files we have. We can get this from  &lt;code&gt;git status --porcelain&lt;/code&gt;. I am going to set these to be red if they are not zero, so they stand out, and be green otherwise. We will also store a list of each file type so we can make a tooltip on the counter to see what is there.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-emacs-lisp"&gt;(&lt;span style="color: #8b0000;"&gt;defun&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;git-parse-status&lt;/span&gt; ()
  (interactive)
  (&lt;span style="color: #8b0000;"&gt;let&lt;/span&gt; ((U 0)   ; &lt;span style="color: #ff0000; font-weight: bold;"&gt;untracked files&lt;/span&gt;
        (M 0)   ; &lt;span style="color: #ff0000; font-weight: bold;"&gt;modified files&lt;/span&gt;
        (O 0)   ; &lt;span style="color: #ff0000; font-weight: bold;"&gt;other files&lt;/span&gt;
        (U-files &lt;span style="color: #228b22;"&gt;""&lt;/span&gt;)
        (M-files &lt;span style="color: #228b22;"&gt;""&lt;/span&gt;)
        (O-files &lt;span style="color: #228b22;"&gt;""&lt;/span&gt;))
    (&lt;span style="color: #8b0000;"&gt;dolist&lt;/span&gt; (line (split-string
                   (shell-command-to-string &lt;span style="color: #228b22;"&gt;"git status --porcelain"&lt;/span&gt;)
                   &lt;span style="color: #228b22;"&gt;"\n"&lt;/span&gt;))
      (&lt;span style="color: #8b0000;"&gt;cond&lt;/span&gt;

       ;; &lt;span style="color: #ff0000; font-weight: bold;"&gt;ignore empty line at end&lt;/span&gt;
       ((string= &lt;span style="color: #228b22;"&gt;""&lt;/span&gt; line) nil)

       ((string-match &lt;span style="color: #228b22;"&gt;"^\\?\\?"&lt;/span&gt; line)
        (setq U (+ 1 U))
        (setq U-files (concat U-files &lt;span style="color: #228b22;"&gt;"\n"&lt;/span&gt; line)))

       ((string-match &lt;span style="color: #228b22;"&gt;"^ M"&lt;/span&gt; line)
        (setq M (+ 1 M))
        (setq M-files (concat M-files &lt;span style="color: #228b22;"&gt;"\n"&lt;/span&gt; line))
        )

       (t
        (message &lt;span style="color: #228b22;"&gt;"detected other in %s"&lt;/span&gt; line)
        (setq O (+ 1 O))
        (setq O-files (concat O-files &lt;span style="color: #228b22;"&gt;"\n"&lt;/span&gt; line)))))
      
    ;; &lt;span style="color: #ff0000; font-weight: bold;"&gt;construct propertized string&lt;/span&gt;
    (concat
     &lt;span style="color: #228b22;"&gt;"("&lt;/span&gt;
     (propertize 
      (format &lt;span style="color: #228b22;"&gt;"M:%d"&lt;/span&gt; M)
      'face (list '&lt;span style="color: #cd0000;"&gt;:foreground&lt;/span&gt; (&lt;span style="color: #8b0000;"&gt;if&lt;/span&gt; (&amp;gt; M 0)
                                   &lt;span style="color: #228b22;"&gt;"red"&lt;/span&gt;
                                 &lt;span style="color: #228b22;"&gt;"forest green"&lt;/span&gt;))
      'help-echo M-files)
     &lt;span style="color: #228b22;"&gt;"|"&lt;/span&gt;
     (propertize 
      (format &lt;span style="color: #228b22;"&gt;"U:%d"&lt;/span&gt; U)
      'face (list '&lt;span style="color: #cd0000;"&gt;:foreground&lt;/span&gt; (&lt;span style="color: #8b0000;"&gt;if&lt;/span&gt; (&amp;gt; U 0)
                                   &lt;span style="color: #228b22;"&gt;"red"&lt;/span&gt;
                                 &lt;span style="color: #228b22;"&gt;"forest green"&lt;/span&gt;))
      'help-echo U-files)
     &lt;span style="color: #228b22;"&gt;"|"&lt;/span&gt;
     (propertize 
      (format &lt;span style="color: #228b22;"&gt;"O:%d"&lt;/span&gt; O)
      'face (list '&lt;span style="color: #cd0000;"&gt;:foreground&lt;/span&gt; (&lt;span style="color: #8b0000;"&gt;if&lt;/span&gt; (&amp;gt; O 0)
                                   &lt;span style="color: #228b22;"&gt;"red"&lt;/span&gt;
                                 &lt;span style="color: #228b22;"&gt;"forest green"&lt;/span&gt;))
      'help-echo O-files)                   
      &lt;span style="color: #228b22;"&gt;") "&lt;/span&gt;)))

(git-parse-status)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
(M:1|U:2|O:0) 
&lt;/pre&gt;

&lt;p&gt;
Finally, let us get the branch we are on, and the commits with respect to a remote. We can do that like this. We use some unicode characters to indicate what direction things go, e.g. an up arrow to indicate you need to push, and a down arrow to indicate you should pull.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-emacs-lisp"&gt;(&lt;span style="color: #8b0000;"&gt;defun&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;git-remote-status&lt;/span&gt; ()
  (interactive)
  (&lt;span style="color: #8b0000;"&gt;let*&lt;/span&gt; (;; &lt;span style="color: #ff0000; font-weight: bold;"&gt;get the branch we are on.&lt;/span&gt;
         (branch (s-trim
                  (shell-command-to-string
                   &lt;span style="color: #228b22;"&gt;"git rev-parse --abbrev-ref HEAD"&lt;/span&gt;)))
         ;; &lt;span style="color: #ff0000; font-weight: bold;"&gt;get the remote the branch points to.&lt;/span&gt;
         (remote (s-trim
                  (shell-command-to-string
                   (format &lt;span style="color: #228b22;"&gt;"git config branch.%s.remote"&lt;/span&gt; branch))))
         (remote-branch (s-trim
                         (shell-command-to-string
                          &lt;span style="color: #228b22;"&gt;"git for-each-ref --format='%(upstream:short)' $(git symbolic-ref -q HEAD)"&lt;/span&gt;)))
         (commits (split-string
                   (s-trim
                    (shell-command-to-string
                     (format
                      &lt;span style="color: #228b22;"&gt;"git rev-list --count --left-right HEAD...%s"&lt;/span&gt;
                      remote-branch)))))
         (local (nth 0 commits))
         (remotes (nth 1 commits)))
    (concat
     &lt;span style="color: #228b22;"&gt;"["&lt;/span&gt;
     (propertize
      (format &lt;span style="color: #228b22;"&gt;"%s"&lt;/span&gt; branch)
      'face (list &lt;span style="color: #cd0000;"&gt;:foreground&lt;/span&gt; &lt;span style="color: #228b22;"&gt;"magenta"&lt;/span&gt;))
     &lt;span style="color: #228b22;"&gt;"|"&lt;/span&gt;
     (format &lt;span style="color: #228b22;"&gt;"&amp;#8593;%s|&amp;#8595;%s"&lt;/span&gt; local remotes)
     &lt;span style="color: #228b22;"&gt;"]"&lt;/span&gt;))) 

(git-remote-status)
&lt;/pre&gt;
&lt;/div&gt;

&lt;pre class="example"&gt;
[source|↑0|↓0]
&lt;/pre&gt;

&lt;p&gt;
Now, we can finally put this together in a little minor mode. We add an element to the mode-line-format variable that evaluates those functions. When we turn off the minor mode, we remove the element from the modeline.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-emacs-lisp"&gt;(&lt;span style="color: #8b0000;"&gt;define-minor-mode&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;git-mode&lt;/span&gt;
  &lt;span style="color: #228b22;"&gt;"minor mode to put git repo status in modeline"&lt;/span&gt;
  nil nil nil
  (&lt;span style="color: #8b0000;"&gt;let&lt;/span&gt; ((git-modeline '(&lt;span style="color: #cd0000;"&gt;:eval&lt;/span&gt; (&lt;span style="color: #8b0000;"&gt;if&lt;/span&gt; (not (in-git-p))
                                  &lt;span style="color: #228b22;"&gt;""&lt;/span&gt;
                                (concat 
                                 (git-remote-status)
                                 (git-parse-status))))))
    (&lt;span style="color: #8b0000;"&gt;if&lt;/span&gt; git-mode
        ;; &lt;span style="color: #ff0000; font-weight: bold;"&gt;put in modeline&lt;/span&gt;
        (push git-modeline mode-line-format)
      ;; &lt;span style="color: #ff0000; font-weight: bold;"&gt;remove from modeline&lt;/span&gt;
      (setq mode-line-format
            (-remove (&lt;span style="color: #8b0000;"&gt;lambda&lt;/span&gt; (x)
                       (equal x git-modeline))                                  
                     mode-line-format)))))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
This leads to a modeline that looks like this (when my mouse is hovered over the M):
&lt;/p&gt;


&lt;div class="figure"&gt;
&lt;p&gt;&lt;img src="/media/2014-09-19-A-git-status-Emacs-modeline/git-modeline.png"&gt; 
&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;
This seems to have some performance issue, since pretty much everytime I type a key, it updates the modeline, and runs git. That is too often. Let us redefine the mode here so we have a minimum time between updates, say 15 seconds. We will store the last time updated, and the last value of the mode-line. Then each time the modeline updates, if the time since the last update is greater than our interval, then we will run the git commands. Otherwise, we just use the old modeline value.
&lt;/p&gt;

&lt;div class="org-src-container"&gt;

&lt;pre class="src src-emacs-lisp"&gt;(&lt;span style="color: #8b0000;"&gt;defvar&lt;/span&gt; &lt;span style="color: #8b008b;"&gt;git-modeline-last-update&lt;/span&gt; (float-time) &lt;span style="color: #228b22;"&gt;"Last time we updated"&lt;/span&gt;)
(&lt;span style="color: #8b0000;"&gt;defvar&lt;/span&gt; &lt;span style="color: #8b008b;"&gt;git-modeline-update-interval&lt;/span&gt; 15 &lt;span style="color: #228b22;"&gt;"Minimum time between update in seconds"&lt;/span&gt;)
(&lt;span style="color: #8b0000;"&gt;defvar&lt;/span&gt; &lt;span style="color: #8b008b;"&gt;git-modeline&lt;/span&gt; &lt;span style="color: #228b22;"&gt;""&lt;/span&gt; &lt;span style="color: #228b22;"&gt;"Last value of the modeline"&lt;/span&gt;)

(&lt;span style="color: #8b0000;"&gt;define-minor-mode&lt;/span&gt; &lt;span style="color: #8b2323;"&gt;git-mode&lt;/span&gt;
  &lt;span style="color: #228b22;"&gt;"minor mode to put git repo status in modeline"&lt;/span&gt;
  nil nil nil
  (&lt;span style="color: #8b0000;"&gt;let&lt;/span&gt; ((git-modeline '(&lt;span style="color: #cd0000;"&gt;:eval&lt;/span&gt; (&lt;span style="color: #8b0000;"&gt;if&lt;/span&gt;
                                  (&amp;gt; (- (float-time) git-modeline-last-update)
                                     git-modeline-update-interval)
                                  ;; &lt;span style="color: #ff0000; font-weight: bold;"&gt;we are updating                              &lt;/span&gt;
                                  (setq git-modeline
                                        (&lt;span style="color: #8b0000;"&gt;if&lt;/span&gt; (not (in-git-p))
                                            &lt;span style="color: #228b22;"&gt;""&lt;/span&gt;                                   
                                          (setq  git-modeline-last-update (float-time))
                                          (concat 
                                           (git-remote-status)
                                           (git-parse-status))))
                                
                              ;; &lt;span style="color: #ff0000; font-weight: bold;"&gt;use last value of the modeline&lt;/span&gt;
                              git-modeline))))
    (&lt;span style="color: #8b0000;"&gt;if&lt;/span&gt; git-mode
        ;; &lt;span style="color: #ff0000; font-weight: bold;"&gt;put in modeline&lt;/span&gt;
        (push git-modeline mode-line-format)
      ;; &lt;span style="color: #ff0000; font-weight: bold;"&gt;remove from modeline&lt;/span&gt;
      (setq mode-line-format
            (-remove (&lt;span style="color: #8b0000;"&gt;lambda&lt;/span&gt; (x)
                       (equal x git-modeline))                                  
                     mode-line-format)))))
&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;
That does it I think. I don't have any performance issues here now. I have not tested this super thoroughly on many git repos, but it seems to be pretty consistent and correct so far. The remote status code is where there is the most probability for issues. I still do not know that part of git very well.  I wonder if there is a more elegant solution than this, perhaps an idle timer. I notice a little lag in updating the data when I switch to another git repo. That might be a little confusing one day.
&lt;/p&gt;


&lt;p&gt;
Otherwise, this seems like a pretty nice solution so far. There are still some things that would be nice to see on here. For example, a pop-up menu on the modeline to switch branches, push or pull, and with actions for the files, e.g. add/commit, etc&amp;#x2026; Those do not seem to hard to &lt;/p&gt;
&lt;p&gt;Copyright (C) 2014 by John Kitchin. See the &lt;a href="/copying.html"&gt;License&lt;/a&gt; for information about copying.&lt;p&gt;&lt;p&gt;&lt;a href="/org/2014/09/19/A-git-status-Emacs-modeline.org"&gt;org-mode source&lt;/a&gt;&lt;p&gt;&lt;p&gt;Org-mode version = 8.2.7c&lt;/p&gt;]]></content:encoded>
    </item>
  </channel>
</rss>
