<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Tips on Clément Joly – Open-Source, Rust &amp; SQLite</title><link>https://joly.pw/tags/tips/</link><description>Recent content in Tips on Clément Joly – Open-Source, Rust &amp; SQLite</description><image><title>Clément Joly – Open-Source, Rust &amp; SQLite</title><url>https://joly.pw/images/open-graph-pages.jpg</url><link>https://joly.pw/images/open-graph-pages.jpg</link></image><generator>Hugo</generator><language>en</language><copyright>Clément Joly</copyright><lastBuildDate>Mon, 04 May 2026 19:41:10 +0100</lastBuildDate><atom:link href="https://joly.pw/tags/tips/index.xml" rel="self" type="application/rss+xml"/><item><title>Advanced filtering in Git commands</title><link>https://joly.pw/blog/pathspec/</link><pubDate>Mon, 04 May 2026 19:09:13 +0100</pubDate><guid>https://joly.pw/blog/pathspec/</guid><description>Learning more about Git pathspec: negative patterns, attributes...</description><content:encoded><![CDATA[



  
  
  
  

  <div class="alert alert-tldr">
    <p class="alert-heading">
      ⚡
      
        TL;DR
      
    </p>
    <p>You can use negative filters like</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git diff <span style="color:#98c379">&#39;:!*.lock&#39;</span>
</span></span><span style="display:flex;"><span>git status <span style="color:#98c379">&#39;:!*.lock&#39;</span>
</span></span></code></pre></div><p>and on attributes:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git status <span style="color:#98c379">&#39;:(attr:!linguist-generated !linguist-vendored)&#39;</span>
</span></span></code></pre></div><p>You can even combine those patterns.</p>
  </div>



<h2 id="excluding-on-a-glob">Excluding on a glob</h2>
<p>I sometimes have many changes over multiple files in a Git repository. And then I want to display only the changes made to files in the <code>src/</code> directory:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git diff <span style="color:#98c379">&#39;*/src/*&#39;</span>
</span></span></code></pre></div><p>Or, more interesting, I want all changes except those made to lock files:</p>
<div class="highlight"><pre tabindex="0" style="color:#abb2bf;background-color:#282c34;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git diff <span style="color:#98c379">&#39;:!*.lock&#39;</span>
</span></span></code></pre></div><p>Note that in the above examples, patterns are quoted, so that the shell does not expand the glob itself.
Here, Git is interpreting the patterns<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>.
This feature is called <a href="https://git-scm.com/docs/gitglossary#def_pathspec">“pathspec”</a>.</p>
<h2 id="diving-into-pathspec">Diving into pathspec</h2>
<p>That feature can be used with commands like <code>git ls-files</code>, <code>git status</code>, <code>git grep</code>&hellip;
And you can do more than exclude globs.</p>
<p>The above <code>:!*.lock</code> is actually a short form of <code>:(exclude)*.lock</code>. And quite a few other keywords exist, like <code>top</code> to force a match on the full path from the root of the repository, <code>icase</code> for a case insensitive match or <code>attr</code> to place particular constraints on the <a href="https://git-scm.com/docs/gitattributes">attributes</a> of a file.</p>
<p>For example, you can use the <a href="https://docs.github.com/en/repositories/working-with-files/managing-files/customizing-how-changed-files-appear-on-github">linguist</a> attributes understood by GitHub and other forges locally with:</p>
<ul>
<li><code>':(attr:!linguist-generated !linguist-vendored)'</code> matches files that have neither of these linguist attributes,</li>
<li><code>':(attr:linguist-generated)' ':(attr:linguist-vendored)'</code> matches files with either attribute<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>,</li>
<li>and that can be combined with file patterns and other attributes, like <code>':(icase,attr:linguist-vendored)*/LOCK'</code> which would match a file named <code>lock</code> or <code>Lock</code> or even <code>LoCk</code> if it has the <code>linguist-vendored</code> attribute.</li>
</ul>
<p>The <a href="https://git-scm.com/docs/gitglossary#def_pathspec">manual</a> contains even more details, like attributes gotchas, matching without interpreting globs&hellip;. Have fun!</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Although, we could remove the quotes and just use the shell in the first example. It’s likely to be slower because the shell would consider all files, while Git can use gitignore, look for changed files first&hellip;&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2">
<p>Usually files don’t have both attributes, but if you wanted to match files with both attributes set, you could use <code>':(attr:linguist-generated linguist-vendored)'</code>.&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</div>
]]></content:encoded></item></channel></rss>