<?xml version="1.0" encoding="utf-8" standalone="yes"?>

<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Thomas Letan's Blog - workflow</title>
    <link>https://soap.coffee/~lthms/tags/workflow.html</link>
    <description>Posts tagged "workflow"</description>
    <atom:link href="https://soap.coffee/~lthms/tags/workflow.xml" rel="self"
               type="application/rss+xml" />
    
    
    <item>
      <title>Using git maintenance with Encrypted SSH Keys</title>
      <link>https://soap.coffee/~lthms/posts/GitMaintenanceSshEncryptedKeys.html</link>
      <guid>https://soap.coffee/~lthms/posts/GitMaintenanceSshEncryptedKeys.html</guid>
      <pubDate>February 18, 2024</pubDate>
      <description>
        
        &lt;h1&gt;Using &lt;code class=&quot;hljs&quot;&gt;git maintenance&lt;/code&gt; with Encrypted SSH Keys&lt;/h1&gt;&lt;div id=&quot;tags-list&quot;&gt;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#tag&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;/~lthms/tags/workflow.html&quot; class=&quot;tag hover-peach&quot; marked=&quot;&quot;&gt;workflow&lt;/a&gt; &lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#tag&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;/~lthms/tags/git.html&quot; class=&quot;tag hover-mint&quot; marked=&quot;&quot;&gt;git&lt;/a&gt; &lt;/div&gt;
&lt;p&gt;This year, I went to &lt;a href=&quot;https://fosdem.org/2024&quot; class=&quot;hover-peach&quot; marked=&quot;&quot;&gt;FOSDEM 2024&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. It was nice and
&lt;s&gt;cosy&lt;/s&gt; crowded, and I really enjoyed my time there. The very last talk I
could attend to before having to leave for the train station was “&lt;a href=&quot;https://www.youtube.com/watch?v=aolI_Rz0ZqY&amp;amp;pp=ygUZc28geW91IHRoaW5rIHlvdSBrbm93IGdpdA%3D%3D&quot; class=&quot;hover-mint&quot; marked=&quot;&quot;&gt;So You Think
You Know
Git&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#youtube&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;”
by &lt;a href=&quot;https://twitter.com/chacon&quot; class=&quot;hover-peach&quot; marked=&quot;&quot;&gt;Scott Chacon&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. If you haven’t already, go and
watch it. It is a very good and educational presentation. You will learn what
&lt;code class=&quot;hljs&quot;&gt;git blame -C -C -C&lt;/code&gt; does and never be the same.&lt;/p&gt;
&lt;p&gt;Another takeaway for me was &lt;code class=&quot;hljs&quot;&gt;git maintenance&lt;/code&gt;. &lt;code class=&quot;hljs&quot;&gt;git maintenance&lt;/code&gt; allows running
in the background a set of tasks which optimize commands like &lt;code class=&quot;hljs&quot;&gt;git add&lt;/code&gt; and
&lt;code class=&quot;hljs&quot;&gt;git fetch&lt;/code&gt; for a responsive user experience. I mean, count me in! Our git
repository at &lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-variable&quot;&gt;$WORK&lt;/span&gt;&lt;/code&gt; has become fairly big over the years&lt;label for=&quot;fn1&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input id=&quot;fn1&quot; type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;Not &lt;a href=&quot;https://youtu.be/aolI_Rz0ZqY?si=-ielyNVXwREJqupo&amp;amp;t=1430&quot; class=&quot;hover-sky&quot; marked=&quot;&quot;&gt;&lt;em&gt;mono-repo&lt;/em&gt;
big&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; yet, but
still big enough to make &lt;code class=&quot;hljs&quot;&gt;git fetch --all --prune&lt;/code&gt; feels… well,
unresponsive. &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;So, when I came back to work the next day, I run the magic command the speaker
had mentioned&lt;label for=&quot;fn2&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input id=&quot;fn2&quot; type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;Following &lt;a href=&quot;https://twitter.com/leostera/status/1740796853174596007&quot; class=&quot;hover-coral&quot; marked=&quot;&quot;&gt;a petition from the
Internet&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;, my
terminal prompt is &lt;code class=&quot;hljs&quot;&gt;;&lt;/code&gt;. &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;; git maintenance start
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This created a bunch of user systemd services and timers which I decided to run
immediately to test that everything was working correctly, starting with the
hourly service responsible for prefetching remote branches.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;; systemctl --user start git-maintenance@hourly.service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, this did not work out, and for predictable reasons.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;; systemctl --user status git-maintenance@hourly.service
(...)
systemd[1706]: Starting Optimize Git repositories data...
git[76228]: git@gitlab.com: Permission denied (publickey).
git[76226]: error: failed to prefetch remotes
git[76226]: error: task &lt;span class=&quot;hljs-string&quot;&gt;&apos;prefetch&apos;&lt;/span&gt; failed
systemd[1706]: git-maintenance@hourly.service: Main process exited, code=exited, status=&amp;gt;
systemd[1706]: git-maintenance@hourly.service: Failed with result &lt;span class=&quot;hljs-string&quot;&gt;&apos;exit-code&apos;&lt;/span&gt;.
systemd[1706]: Failed to start Optimize Git repositories data.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The culprit here is the fact I am using an encrypted SSH key to connect to
Gitlab where our repository is hosted and out of the box the scripts run by
&lt;code class=&quot;hljs&quot;&gt;git-maintenance&lt;/code&gt; have now way to use them. This is because &lt;code class=&quot;hljs&quot;&gt;git-maintenance&lt;/code&gt;
is not aware of the existence of the SSH agent running on my laptop.&lt;/p&gt;
&lt;p&gt;The solution can be read in the Man page of &lt;code class=&quot;hljs&quot;&gt;git-maintenance&lt;/code&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;(…) any customization should be done by creating a drop-in file, &lt;em&gt;i.e.&lt;/em&gt; a
&lt;code class=&quot;hljs&quot;&gt;.conf&lt;/code&gt; suffixed file in the
&lt;code class=&quot;hljs&quot;&gt;~/.config/systemd/user/git-maintenance@.service.d&lt;/code&gt; directory.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I didn’t know this general purpose trick which should work for any systemd
service running on a Linux machine! Thanks, anonymous technical writer who took
the time to contribute to this Man page.&lt;/p&gt;
&lt;p&gt;And indeed, creating a file named &lt;code class=&quot;hljs&quot;&gt;10-ssh.conf&lt;/code&gt;&lt;label for=&quot;fn3&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input id=&quot;fn3&quot; type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;The prefix number is as important as the &lt;code class=&quot;hljs&quot;&gt;.conf&lt;/code&gt; suffix mentioned in
the &lt;code class=&quot;hljs&quot;&gt;git-maintenance&lt;/code&gt; Man page for systemd to load the drop-in file. &lt;/span&gt;
&lt;/span&gt; in
&lt;code class=&quot;hljs&quot;&gt;${HOME}/.config/systemd/user/git-maintenance@.service.d/&lt;/code&gt; to set the
&lt;code class=&quot;hljs&quot;&gt;SSH_AUTH_SOCK&lt;/code&gt; environment variable solved my issue. Its value depends on your
personal setup. In my case, I am using the systemd &lt;code class=&quot;hljs&quot;&gt;ssh-agent.service&lt;/code&gt; user
unit.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;; systemctl --user show ssh-agent.service | grep Environment=SSH_AUTH_SOCK
Environment=SSH_AUTH_SOCK=/run/user/1000/ssh-agent.socket
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We replicate this in our &lt;code class=&quot;hljs&quot;&gt;10-ssh.conf&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-systemd&quot;&gt;[Service]
Environment=SSH_AUTH_SOCK=/run/user/1000/ssh-agent.socket
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we are done! This time, executing the service manually will work (assuming
the necessary encrypted key has been &lt;code class=&quot;hljs&quot;&gt;ssh-add&lt;/code&gt; to the agent).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;; systemctl --user daemon-reload
; systemctl --user start git-maintenance@hourly.service
; systemctl --user status git-maintenance@hourly.service
(...)
systemd[1706]: Starting Optimize Git repositories data...
systemd[1706]: Finished Optimize Git repositories data.
&lt;/code&gt;&lt;/pre&gt;
        
      </description>
    </item>
    
    
    
    <item>
      <title>How I Keep Using Stacked Git at $WORK</title>
      <link>https://soap.coffee/~lthms/posts/StackedGit2.html</link>
      <guid>https://soap.coffee/~lthms/posts/StackedGit2.html</guid>
      <pubDate>January 16, 2023</pubDate>
      <description>
        
        &lt;h1&gt;How I Keep Using Stacked Git at &lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-variable&quot;&gt;$WORK&lt;/span&gt;&lt;/code&gt;&lt;/h1&gt;&lt;div id=&quot;tags-list&quot;&gt;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#tag&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;/~lthms/tags/git.html&quot; class=&quot;tag hover-periwinkle&quot; marked=&quot;&quot;&gt;git&lt;/a&gt; &lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#tag&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;/~lthms/tags/workflow.html&quot; class=&quot;tag hover-mint&quot; marked=&quot;&quot;&gt;workflow&lt;/a&gt; &lt;/div&gt;
&lt;p&gt;One year ago, I have published an article summarizing &lt;a href=&quot;/~lthms/posts/StackedGit.html&quot; class=&quot;hover-lavender&quot; marked=&quot;&quot;&gt;my experience using
Stacked Git at &lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-variable&quot;&gt;$WORK&lt;/span&gt;&lt;/code&gt;&lt;/a&gt;. Twelve months later,
enough has changed to motivate a spin-off piece.&lt;/p&gt;
&lt;h2&gt;Stacked Git is &lt;em&gt;Fast&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;Firstly, it is important to state that my main complaint about
Stacked Git is now a thing of the past&lt;label for=&quot;fn1&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input id=&quot;fn1&quot; type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;For fairness, I have removed the related section in my previous
write-up. &lt;/span&gt;
&lt;/span&gt;! Stacked Git does not feel slow
anymore, and far from it. This is because &lt;a href=&quot;https://github.com/stacked-git/stgit/discussions/185&quot; class=&quot;hover-coral&quot; marked=&quot;&quot;&gt;Stacked Git 2.0 has been rewritten
in Rust&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. While RiiR
(&lt;em&gt;Rewrite it in Rust&lt;/em&gt;) is a running meme on the Internet, in this particular
case, the result is very exciting.&lt;/p&gt;
&lt;p&gt;Thanks to the performance boost, my Zsh prompt does not take 0.1s to
appear!&lt;/p&gt;
&lt;p&gt;Speaking of Zsh prompt, basically what I ended up displaying is &lt;code class=&quot;hljs&quot;&gt;(&amp;lt;TOP PATCH NAME&amp;gt; &amp;lt;APPLIED PATCHES COUNT&amp;gt;/&amp;lt;PATCHSET SIZE&amp;gt; &amp;lt;HIDDEN PATCHES COUNT)&lt;/code&gt;. For
instance, &lt;code class=&quot;hljs&quot;&gt;(fix-1337 1/2 3)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In case you want to take inspiration in my somewhat working configuration, here
is the snippet of interest.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-built_in&quot;&gt;local&lt;/span&gt; series_top=&lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-subst&quot;&gt;$(stg top 2&amp;gt; /dev/null)&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-built_in&quot;&gt;local&lt;/span&gt; total=&lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-subst&quot;&gt;$(stg series 2&amp;gt; /dev/null | wc -l)&lt;/span&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-built_in&quot;&gt;local&lt;/span&gt; hidden=&lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-subst&quot;&gt;$(stg series --hidden 2&amp;gt; /dev/null | wc -l)&lt;/span&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; [[ &lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-variable&quot;&gt;${total}&lt;/span&gt;&quot;&lt;/span&gt; -gt 0 ]]; &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;hljs-built_in&quot;&gt;local&lt;/span&gt; not_applied=&lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-subst&quot;&gt;$(stg series | grep -E &apos;^-&apos; | wc -l)&lt;/span&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-built_in&quot;&gt;local&lt;/span&gt; applied=&lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-subst&quot;&gt;$(($total - $not_applied)&lt;/span&gt;)&quot;&lt;/span&gt;

    &lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; [[ -z &lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-variable&quot;&gt;${series_top}&lt;/span&gt;&quot;&lt;/span&gt; ]]; &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt;
        series_top=&lt;span class=&quot;hljs-string&quot;&gt;&quot;·&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-keyword&quot;&gt;fi&lt;/span&gt;

    &lt;span class=&quot;hljs-built_in&quot;&gt;echo&lt;/span&gt; -n &lt;span class=&quot;hljs-string&quot;&gt;&quot;(&lt;span class=&quot;hljs-variable&quot;&gt;${status_color}&lt;/span&gt;&lt;span class=&quot;hljs-variable&quot;&gt;${series_top}&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;${applied}&lt;/span&gt;/&lt;span class=&quot;hljs-variable&quot;&gt;${total}&lt;/span&gt; &lt;span class=&quot;hljs-variable&quot;&gt;${hidden}&lt;/span&gt;)&quot;&lt;/span&gt;
    &lt;span class=&quot;hljs-built_in&quot;&gt;echo&lt;/span&gt; -n &lt;span class=&quot;hljs-string&quot;&gt;&quot;  (&lt;span class=&quot;hljs-subst&quot;&gt;$(current_branch)&lt;/span&gt;)&quot;&lt;/span&gt;
&lt;span class=&quot;hljs-keyword&quot;&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Branchless Workflow&lt;/h2&gt;
&lt;p&gt;Last year, I was using Stacked Git on top of git branches. More precisely, I
had one branch for each (stack of) Merge Request. It worked well, because my
typical MR counted 3 to 4 commits in average.&lt;/p&gt;
&lt;p&gt;Fast forward today, and things have changed on this front too. In a nutshell, I
have become a “one commit per MR” maximalist of sort&lt;label for=&quot;fn2&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input id=&quot;fn2&quot; type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;It goes without saying that this approach comes with its set of
drawbacks too.&lt;/span&gt;
&lt;span class=&quot;footnote-p&quot;&gt;During the past year, I’ve pushed fairly large commits which could have
been split into several smaller ones, for the sake of keeping my “one
commit per MR” policy. I have also had to manage large stacks of MRs. &lt;/span&gt;
&lt;/span&gt;. I find this
approach very effective to get more focused reviews, and to reduce the time it
takes for a given MR to be integrated into the main branch.&lt;/p&gt;
&lt;p&gt;My previous approach based on git branches did not scale well with
this new mindset, and during the course of the year, I stopped using
branches altogether&lt;label for=&quot;fn3&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input id=&quot;fn3&quot; type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;I have not invented the branchless workflow, of
course.&lt;/span&gt;
&lt;span class=&quot;footnote-p&quot;&gt;After it was first published, someone posted a link to my Stacked Git
article on Hacker News, and &lt;a href=&quot;https://news.ycombinator.com/item?id=29959224&quot; class=&quot;hover-rose&quot; marked=&quot;&quot;&gt;&lt;em&gt;@arxanas&lt;/em&gt; posted a comment about
&lt;code class=&quot;hljs&quot;&gt;git-branchless&lt;/code&gt;&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. I tried
the tool, and even if it never clicked for me, I was really compelled by
its core ideas.&lt;/span&gt;
&lt;span class=&quot;footnote-p&quot;&gt;Similarly, &lt;a href=&quot;https://drewdevault.com/2020/04/06/My-weird-branchless-git-workflow.html&quot; class=&quot;hover-sky&quot; marked=&quot;&quot;&gt;Drew DeVault has published a complete article on its own
branchless workflow in
2020&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;These days, I proceed as follows.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I name each patch after the branch to which I will push it on our
upstream Git remote.&lt;/li&gt;
&lt;li&gt;99% of the time, I push my work using &lt;code class=&quot;hljs language-bash&quot;&gt;git push -f upstream @:$(stg top)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;I created a small git plugin I called &lt;code class=&quot;hljs&quot;&gt;git-prepare&lt;/code&gt; which allows
me to select one of the patch of my current patchset using &lt;code class=&quot;hljs&quot;&gt;fzf&lt;/code&gt;,
and which pops all other patches that are currently applied.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;code class=&quot;hljs&quot;&gt;git-prepare&lt;/code&gt; is really straightforward:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-meta&quot;&gt;#!/bin/sh&lt;/span&gt;
patch=$(stg series -P | fzf)

&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; [[ ! $? -eq 0 ]] ; &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt;
    &lt;span class=&quot;hljs-built_in&quot;&gt;exit&lt;/span&gt; $?
&lt;span class=&quot;hljs-keyword&quot;&gt;fi&lt;/span&gt;

&lt;span class=&quot;hljs-keyword&quot;&gt;if&lt;/span&gt; [ -n &lt;span class=&quot;hljs-string&quot;&gt;&quot;&lt;span class=&quot;hljs-subst&quot;&gt;$(stg series -A)&lt;/span&gt;&quot;&lt;/span&gt; ]; &lt;span class=&quot;hljs-keyword&quot;&gt;then&lt;/span&gt;
    stg pop -a
&lt;span class=&quot;hljs-keyword&quot;&gt;fi&lt;/span&gt;

stg push &lt;span class=&quot;hljs-variable&quot;&gt;${patch}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main hurdle which I still need to figure out is how to deal with
stacked MRs. Currently, this is very manual. I need to remember
which commit belongs to the stack, the order and dependencies of
these commits, and I need to publish each commit individually using
&lt;code class=&quot;hljs language-bash&quot;&gt;stg push; git push @:$(stg top)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The pragmatic answer is definitely to come back to git branches &lt;em&gt;for
this particular use case&lt;/em&gt;, but it&apos;s not the &lt;em&gt;fun&lt;/em&gt; answer. So from
time to time, I try to experiment with alternative approaches. My current
intuition is that, by adopting a naming convention for my patches, I
could probably implement a thin tooling on top of Stacked Git to
deal with dependents commits.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Putting aside stacked MRs for now, I am really satisfied with my
workflow. It’s very lightweight and intuitive, and working without
Stacked Git now feels backward and clunky.&lt;/p&gt;
&lt;p&gt;So I will take this opportunity to thank one more time Stacked Git’s
authors and contributors. You all are making my professional like
easier with your project.&lt;/p&gt;
        
      </description>
    </item>
    
    
    
    <item>
      <title>How I Use Stacked Git at $WORK</title>
      <link>https://soap.coffee/~lthms/posts/StackedGit.html</link>
      <guid>https://soap.coffee/~lthms/posts/StackedGit.html</guid>
      <pubDate>January 16, 2022</pubDate>
      <description>
        
        &lt;h1&gt;How I Use Stacked Git at &lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-variable&quot;&gt;$WORK&lt;/span&gt;&lt;/code&gt;&lt;/h1&gt;&lt;div id=&quot;tags-list&quot;&gt;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#tag&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;/~lthms/tags/git.html&quot; class=&quot;tag hover-coral&quot; marked=&quot;&quot;&gt;git&lt;/a&gt; &lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#tag&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&amp;nbsp;&lt;a href=&quot;/~lthms/tags/workflow.html&quot; class=&quot;tag hover-periwinkle&quot; marked=&quot;&quot;&gt;workflow&lt;/a&gt; &lt;/div&gt;
&lt;p&gt;According to &lt;a href=&quot;https://lobste.rs/s/s6quvg/stacked_git&quot; class=&quot;hover-peach&quot; marked=&quot;&quot;&gt;my Lobste.rs history&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;, I
have run into &lt;a href=&quot;https://stacked-git.github.io&quot; class=&quot;hover-sky&quot; marked=&quot;&quot;&gt;Stacked Git&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; in early April
2021, and I remember that its promises hit a soft spot. A few weeks later, I was
submitting &lt;a href=&quot;https://github.com/stacked-git/stgit/pull/100&quot; class=&quot;hover-coral&quot; marked=&quot;&quot;&gt;a &lt;em&gt;pull request&lt;/em&gt; to teach Stacked Git to sign
commits&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#github&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;. It was all I needed to
start using it at &lt;code class=&quot;hljs language-bash&quot;&gt;&lt;span class=&quot;hljs-variable&quot;&gt;$WORK&lt;/span&gt;&lt;/code&gt;, and now it has become a cornerstone of my
development workflow.&lt;/p&gt;
&lt;h2&gt;What is Stacked Git?&lt;/h2&gt;
&lt;p&gt;Before going any further, it is probably a good idea to take a moment and
present Stacked Git. The website introduces the tool as follows:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Stacked Git, &lt;em&gt;StGit&lt;/em&gt; for short, is an application for managing Git
commits as a stack of patches.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There are a few things to unpack here. First and as its name suggests, Stacked
Git is a tool built on top of Git&lt;label for=&quot;fn1&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input id=&quot;fn1&quot; type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;My main takeaway from my Pijul adventure is connected to this. Git
is not limited to the &lt;code class=&quot;hljs&quot;&gt;git&lt;/code&gt; binary. Git comes with a collection of powerful
forges, nice editor plugins, and years of good practices.&lt;/span&gt;
&lt;span class=&quot;footnote-p&quot;&gt;To this day, it’s neither the bugs nor the breaking changes that made me
quite Pijul. Those were expected. What I naively did not anticipate is the
dry feeling that Pijul was just the &lt;code class=&quot;hljs&quot;&gt;pijul&lt;/code&gt; binary, which left me with a
lot of tasks to do manually. &lt;/span&gt;
&lt;/span&gt;. It is &lt;em&gt;not&lt;/em&gt; a brand new VCS, and as
a consequence you keep using all your existing tools and plugins&lt;label for=&quot;fn2&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input id=&quot;fn2&quot; type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;I am looking at you, Magit. &lt;/span&gt;
&lt;/span&gt;.
Secondly, Stacked Git helps you curate your Git history, by turning your
commits into patches, and your branches into stacks of patches. This speaks to
me, maybe because I have been fascinated by email-based workflows for quite
some time.&lt;/p&gt;
&lt;p&gt;To me, the two core features of Stacked Git are (1) allowing you to
name your commits, and (2) to navigate among them.
Together, they create a wonderful companion to help you keep your
history clean.&lt;/p&gt;
&lt;h2&gt;My Subset of Stacked Git&lt;/h2&gt;
&lt;p&gt;I do not want this article to be a Stacked Git tutorial.
Fortunately, I don’t really use the tool at its full potential.
I only care about a relatively small subset of commands I feel
comfortable with and use daily.&lt;/p&gt;
&lt;p&gt;First, to decide which commits are part of my “stack of patches,” I
can count of these commands:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;hljs&quot;&gt;stg new NAME&lt;/code&gt; creates an empty commit, and gives it the name
&lt;code class=&quot;hljs&quot;&gt;NAME&lt;/code&gt;.
Having a way to identify a patch with a meaningful name that is
resistant to rebase and amend is very nice.
These are two properties commit hashes do not have.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;hljs&quot;&gt;stg uncommit NAME&lt;/code&gt; names the most recent commit under my
stack with &lt;code class=&quot;hljs&quot;&gt;NAME&lt;/code&gt; and integrates it into it. I do this when I am
tasked to work on a merge request made by a colleague, for
instance.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;hljs&quot;&gt;stg commit&lt;/code&gt; removes from my stack its last patch. I do this when
said commit has been merged into &lt;code class=&quot;hljs&quot;&gt;master&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once my stack of patches is ready, the fun begins.&lt;/p&gt;
&lt;p&gt;At a given time, a patch can either be (1) applied, (2) unapplied, or (3)
hidden. On the one hand, if a patch is applied it is part of the Git history.
On the other hand, unapplying a patch means removing it from the working branch
(but not from the stack of patches of Stacked Git). If a patch becomes
irrelevant, but you don’t want to remove it entirely because it can become
handy later, you can hide it. A hidden patch sits beside the stack of patches,
and can be reintegrated if need be.&lt;/p&gt;
&lt;p&gt;Analogous to &lt;code class=&quot;hljs&quot;&gt;git log&lt;/code&gt; ---which allows you to visualize your Git history---,
&lt;code class=&quot;hljs&quot;&gt;stg series&lt;/code&gt; gives you a view of the state of your stack of patches. Patches
prefixed with &lt;code class=&quot;hljs&quot;&gt;+&lt;/code&gt; (or &lt;code class=&quot;hljs&quot;&gt;&amp;gt;&lt;/code&gt;) are applied, while &lt;code class=&quot;hljs&quot;&gt;-&lt;/code&gt; means the patch is unapplied.&lt;/p&gt;
&lt;p&gt;Then,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;hljs&quot;&gt;stg pop&lt;/code&gt; unapplies the patch on top of the list of applied
patches.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;hljs&quot;&gt;stg push&lt;/code&gt; applies the patch on the bottom of the list of unapplied
patches.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;hljs&quot;&gt;stg goto NAME&lt;/code&gt; unapplies or applies the necessary patches so that
&lt;code class=&quot;hljs&quot;&gt;NAME&lt;/code&gt; becomes the top patch of the list of applied patches.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both &lt;code class=&quot;hljs&quot;&gt;HEAD&lt;/code&gt; and the work tree are updated accordingly.&lt;/p&gt;
&lt;p&gt;In addition, &lt;code class=&quot;hljs&quot;&gt;stg sink&lt;/code&gt; and &lt;code class=&quot;hljs&quot;&gt;stg float&lt;/code&gt; allow reorganizing your
stack of patches, moving patches around.
Basically, they are like &lt;code class=&quot;hljs&quot;&gt;git rebase -i&lt;/code&gt;, but without having to use
&lt;code class=&quot;hljs&quot;&gt;$EDITOR&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Modifying patches is done with &lt;code class=&quot;hljs&quot;&gt;stg refresh&lt;/code&gt;.
It’s akin to &lt;code class=&quot;hljs&quot;&gt;git commit --amend&lt;/code&gt;, except it is more powerful because
you can modify any applied patches with the &lt;code class=&quot;hljs&quot;&gt;-p&lt;/code&gt; option.
I’d always encourage you to &lt;code class=&quot;hljs&quot;&gt;stg goto&lt;/code&gt; first, because &lt;code class=&quot;hljs&quot;&gt;stg refresh -p&lt;/code&gt; remains unfortunately error-prone (nothing prevents you from targeting
the wrong patch).
But when used carefully, it can be very handy.&lt;/p&gt;
&lt;p&gt;Finally, &lt;code class=&quot;hljs&quot;&gt;stg rebase REF&lt;/code&gt; moves your stack of patches on top of &lt;code class=&quot;hljs&quot;&gt;REF&lt;/code&gt;&lt;label for=&quot;fn3&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input id=&quot;fn3&quot; type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;Stacked Git is supposedly able to detect, during a rebase, which of
your patches have been applied to your target branch. I’d rather use &lt;code class=&quot;hljs language-bash&quot;&gt;stg uncommit&lt;/code&gt; before doing the rebase, though. &lt;/span&gt;
&lt;/span&gt;.
It is akin to &lt;code class=&quot;hljs&quot;&gt;git rebase --onto&lt;/code&gt;, but more straightforward. What happens is
Stacked Git pop all the patches of my stack, reset the &lt;code class=&quot;hljs&quot;&gt;HEAD&lt;/code&gt; of the current
branch to &lt;code class=&quot;hljs&quot;&gt;REF&lt;/code&gt;, and tries applying the patches one by one In case of
conflicts, the process stop, and I am left with an empty patch, and a dirty
work tree with conflicts to solve. The hidden gem is that, contrary to &lt;code class=&quot;hljs&quot;&gt;git rebase&lt;/code&gt;, the repository is not “in the middle of a rebase.”&lt;/p&gt;
&lt;p&gt;Suppose there are many conflicting patches still waiting in my stack of patches,
and an urgent task I need to take care of first. I can just leave them here. I
can switch to another branch, and when I come back, I get my patches back. I
call this feature “incremental rebases.”&lt;/p&gt;
&lt;p&gt;And that is basically it. In a nutshell, Stacked Git equips commits with the
same features as branches.&lt;/p&gt;
&lt;h2&gt;My Stacked Git Workflow&lt;/h2&gt;
&lt;p&gt;As mentioned in the introduction of this article, Stacked Git has become a
cornerstone of my workflow. I’ve been asked a few times what this workflow is,
and why Magit is not enough&lt;label for=&quot;fn4&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input id=&quot;fn4&quot; type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-left sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;It’s always about Magit. ;) &lt;/span&gt;
&lt;/span&gt;. So let’s try to do that. But first, a
warning. Yes, because Stacked Git is only a wrapper above Git, everything I
will explain can be achieved using Git alone, especially if you are a Magit
wizard.&lt;/p&gt;
&lt;p&gt;Stacked Git makes just everything so more convenient to me.&lt;/p&gt;
&lt;h3&gt;Planning My Commits Ahead Of Time&lt;/h3&gt;
&lt;p&gt;I’ve been introduced to Git with a pretty simple workflow: I am
supposed to start working on a feature, and once it’s ready, I
can commit, and move on to the next task on my to-do list.&lt;/p&gt;
&lt;p&gt;To me, this approach is backward.
It makes you set your intent after the fact.
With Stacked Git, I often try to plan my final history /before
writing the very first line of code/.
Using &lt;code class=&quot;hljs&quot;&gt;stack new&lt;/code&gt;, I create my patches, and take the time to write
their description.
It helps me visualize where I want to go.
Then, I use &lt;code class=&quot;hljs&quot;&gt;stack goto&lt;/code&gt; to go back to the beginning of my stack,
and start working.&lt;/p&gt;
&lt;p&gt;It is not, and cannot be, an exact science. I often have to refine
them as my work progresses.
Yet, I think my Git history is cleaner, more focused, since I have
started this exercise.&lt;/p&gt;
&lt;h3&gt;Getting My Fixup Commits Right&lt;/h3&gt;
&lt;p&gt;Reviews are a fundamental aspect of a software developer job.
At &lt;code class=&quot;hljs&quot;&gt;$WORK&lt;/code&gt;, we use Gitlab and their merge requests workflow,
which I find very annoying, because it does not provide meaningful
ways to compare two versions of your submission&lt;label for=&quot;fn5&quot; class=&quot;sidenote-number margin-toggle&quot;&gt;&lt;/label&gt;&lt;input id=&quot;fn5&quot; type=&quot;checkbox&quot; class=&quot;margin-toggle&quot;&gt;&lt;span class=&quot;note-right sidenote note&quot;&gt;&lt;span class=&quot;footnote-p&quot;&gt;There is a notion of “versions” in Gitlab, but its ergonomics fall
short of my expectations for such a tool. &lt;/span&gt;
&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;What we end up doing is creating “fixup commits,” and we push them
to Gitlab so that reviewers can easily verify that their feedback
has correctly been taken into account.&lt;/p&gt;
&lt;p&gt;A fixup commit is a commit that will eventually be squashed into
another.
You can understand it as a delayed &lt;code class=&quot;hljs&quot;&gt;git commit --amend&lt;/code&gt;.
Git has some built-in features to manipulate them.
You create them with &lt;code class=&quot;hljs&quot;&gt;git commit --fixup=&amp;lt;HASH&amp;gt;&lt;/code&gt;, and they are
interpreted in a specific manner by &lt;code class=&quot;hljs&quot;&gt;git rebase -i&lt;/code&gt;.
But they have always felt to me like a sordid hack.
It is way too easy to create a fixup commit that targets the wrong
commit, and you can end up with strange conflicts when you finally
squash them.
That being said, if used carefully, they are a powerful tool to
keep a Git history clean.&lt;/p&gt;
&lt;p&gt;I am not sure we are using them carefully, though.&lt;/p&gt;
&lt;p&gt;Some reviews can be excruciating, with dozens of comments to
address, and theoretically as many fixup commits to create.
Then you push all of them on Gitlab, and days later, after the
green light from the reviewer, you get to call &lt;code class=&quot;hljs&quot;&gt;git rebase&lt;/code&gt;
and discover your history is broken, you have tones of conflicts
to fix, and you’re good for a long afternoon of untangling.&lt;/p&gt;
&lt;p&gt;The main reason behind this mess is that you end up fixing a commit
from the &lt;code class=&quot;hljs&quot;&gt;HEAD&lt;/code&gt; of your working branch, not the commit itself.
But with Stacked Git, things are different.
With &lt;code class=&quot;hljs&quot;&gt;stg goto&lt;/code&gt;, I put my working tree in the best state possible
to fix a commit: the commit itself.
I can use &lt;code class=&quot;hljs&quot;&gt;stg new&lt;/code&gt; to create a fixup commit, with a meaningful
name.
Then, I am forced to deal with the potential conflicts it brings
when I call &lt;code class=&quot;hljs&quot;&gt;stg push&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once my reviewer is happy with my work, I can call &lt;code class=&quot;hljs&quot;&gt;stg squash&lt;/code&gt;.
It is less automated than &lt;code class=&quot;hljs&quot;&gt;git rebase -i&lt;/code&gt;, but the comfort I gained
during the development is worth this little annoyance.&lt;/p&gt;
&lt;h3&gt;Managing Stacked Merge Requests&lt;/h3&gt;
&lt;p&gt;At &lt;code class=&quot;hljs&quot;&gt;$WORK&lt;/code&gt;, we are trying to change how we deliver new features to
our &lt;code class=&quot;hljs&quot;&gt;master&lt;/code&gt; branch.
More precisely, we want to merge smaller contributions more
frequently.
We have had our fair share of large and complex merge requests that
were a nightmare to review in the past, and it’s really not a fun
position to be put in.&lt;/p&gt;
&lt;p&gt;For a few months, I have been involved in a project wherein we
decided /not/ to fall in the same trap again.
We agreed on a “planning of merge requests” and started working.
The first merge request was soon opened.
We’ve nominated an “owner” to take care of the review, and the rest
of the team carried on.
Before the first merge request was merged, the second one was
declared ready, and another owner was appointed.
Then, the owner of the first merge request had a baby, and yours
truly ended up having to manage two interdependent merge requests.&lt;/p&gt;
&lt;p&gt;It turns out Stacked Git is a wonderful tool to help me keep this
under control.&lt;/p&gt;
&lt;p&gt;I only have one branch, and I use the same workflow to deal with
feedback, even if they are coming from more than one merge
request.
To remember the structure of everything, I just prefix the name of
my patches with a merge request nickname.
So my stack will look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;hljs&quot;&gt;+ mr1-base
+ mr1-tests
+ mr1-doc
&amp;gt; mr2-command
- mr2-tests
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A reviewer leaves a hard-truth comment that requires a significant rework of
the oldest merge request? &lt;code class=&quot;hljs&quot;&gt;stg goto&lt;/code&gt; reverts my work tree in the appropriate
state, and &lt;code class=&quot;hljs&quot;&gt;stg push&lt;/code&gt; allows me to deal with conflicts one patch at a time. If
I need to spend more time on the oldest merge request at some point, I can
continue my work, knowing the patches related to the newest one are awaiting in
my stack.&lt;/p&gt;
&lt;p&gt;The most annoying part is when the time comes to push everything. I need to
&lt;code class=&quot;hljs&quot;&gt;stg goto&lt;/code&gt; at the last patch of each merge request, and &lt;code class=&quot;hljs&quot;&gt;git push HEAD:the-branch&lt;/code&gt;. It’s not horrible. But I will probably try to automate it at
some point.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Overall, I am really thankful to Stacked Git’s authors! Thank you! You are
making my interactions with Git fun and carefree. You provide me some of the
convenience of patch-based VCS like &lt;a href=&quot;http://darcs.net&quot; class=&quot;hover-mint&quot; marked=&quot;&quot;&gt;Darcs&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt; and
&lt;a href=&quot;https://pijul.org&quot; class=&quot;hover-lavender&quot; marked=&quot;&quot;&gt;Pijul&amp;nbsp;&lt;span class=&quot;icon&quot;&gt;&lt;svg&gt;&lt;use href=&quot;/~lthms/img/icons.svg#external-link&quot;&gt;&lt;/use&gt;&lt;/svg&gt;&lt;/span&gt;&lt;/a&gt;, but without sacrificing the power of Git.&lt;/p&gt;
&lt;p&gt;I encourage anyone to at least give it a try, and I really hope I
will be able to contribute back to Stacked Git in the near future.&lt;/p&gt;
        
      </description>
    </item>
    
    
  </channel>
</rss>
