tpwo.github.iohttps://tpwo.github.io/2025-06-30T14:32:00+02:00A personal blogUseful Git settings2025-05-31T00:31:00+02:002025-06-30T14:32:00+02:00tpwotag:tpwo.github.io,2025-05-31:/blog/2025/05/31/useful-git-settings<section id="preamble" aria-label="Preamble"><p>In my dotfiles I always have a <code>.gitconfig</code> file
with some of the settings preconfigured.
Without them using Git for …</p></section><section id="preamble" aria-label="Preamble"><p>In my dotfiles I always have a <code>.gitconfig</code> file
with some of the settings preconfigured.
Without them using Git for me would be much more cumbersome.
Here’s a list of them along with some comments and sources.</p></section>
<section class="doc-section level-1"><h2 id="_core_settings">Core settings</h2><section class="doc-section level-2"><h3 id="_core_autocrlf"><code>core.autocrlf</code></h3><p>Line ending characters are one big mess due to backwards compatibility.
Windows systems use <code>CRLF</code>, Unix and consequently Linux uses <code>LF</code>.
More than that, older Macs used <code>CR</code>,
so the only missing option was <code>LFCR</code> 🤠.
Thankfully Apple switched to <code>LF</code> with Mac OS X 10.0,
so we’re left with two variants.<a class="footnote-ref" id="_footnoteref_1" href="proxy.php?url=#_footnote_1" title="View footnote 1" role="doc-noteref">[1]</a></p>
<p>Git was created by Linus Torvalds
so it’s no surprise that it prefers <code>LF</code> by default,
and warns you if you try committing files with other line ending characters.</p>
<p>Theoretically you can stick to e.g. <code>CRLF</code>
if you’re doing a strictly Windows project,
but I find it much easier to always commit <code>LF</code>,
and configure Git (and my CI<a class="footnote-ref" id="_footnoteref_2" href="proxy.php?url=#_footnote_2" title="View footnote 2" role="doc-noteref">[2]</a>) to guarantee that.</p>
<p>In practice, you might notice that a lot of files
in a lot of repositories in your project
were already committed with <code>CRLF</code>. Such is life.
Because of that I don’t like setting <code>autocrlf</code> to <code>true</code>,
as it tells Git to always convert from <code>CRLF</code> to <code>LF</code> when doing a commit.</p>
<p>This is a bit annoying,
as then it shows that the whole file was modified
(which is in fact true) in the commit view,
and it makes much harder to see the actual changes.
This depends on the tool you’re using
as some of them can hide line ending changes pretty well.
Still, I would advocate to isolate line ending changes from other types of changes.</p>
<p>E.g. you might want to do a single commit in a repo
in which you clean it up for <em>all files</em><a class="footnote-ref" id="_footnoteref_3" href="proxy.php?url=#_footnote_3" title="View footnote 3" role="doc-noteref">[3]</a>,
and then add this commit to <code>.git-blame-ignore-revs</code>,
so it doesn’t makes mess with the history regardless of the tool
and/or settings you are using.</p>
<section class="doc-section level-3"><h4 id="_tldr">TLDR</h4><p>My rule of thumb:</p>
<div class="listing-block"><pre>git config --global core.autocrlf true <b class="conum">1</b>
git config --global core.autocrlf input <b class="conum">2</b></pre><ol class="callout-list arabic"><li>Use that setting on Windows</li><li>Use that setting otherwise (Linux, Mac, WSL)</li></ol></div>
</section></section>
<section class="doc-section level-2"><h3 id="_core_excludesfile"><code>core.excludesFile</code></h3><p>Have you noticed that due to your setup,
i.e. editor or IDE you’re using,
you are the person who always adds particular entries to <code>.gitignore</code> in all repos?
Are you tired of that?
<code>excludesFile</code> is the solution for you.
It’s a globally defined <code>.gitignore</code>
which Git uses even if a <code>.gitignore</code> is missing in a repo.</p>
<p>I like to keep it in my home folder along with <code>.gitconfig</code>,
and I use the name <code>.gitignore_global</code>:</p>
<div class="listing-block"><pre>git config --global core.excludesFile '~/.gitignore_global'</pre></div></section>
<section class="doc-section level-2"><h3 id="_core_sshcommand"><code>core.sshcommand</code></h3><p>I’m a Windows user, but I prefer bash.
As terminal is very important in my workflow,
I use WSL both at work and in my private projects.
There are a lot of quirks about WSL
and one of them is having Git remember you SSH passwords.
There are Linux solutions for that,
and I experimented with them.
Unfortunately, it wasn’t working out great for me
(but maybe something has changed in recent years).
Another solution is to use password-less keys,
but <strong><em>you don’t want to do that, right?</em></strong></p>
<p>Thankfully we can use Windows built-in SSH agent
to remember passwords for us even in WSL.<a class="footnote-ref" id="_footnoteref_4" href="proxy.php?url=#_footnote_4" title="View footnote 4" role="doc-noteref">[4]</a></p>
<p>To do that, we have to tell WSL Git to use SSH binary from Windows:</p>
<div class="listing-block"><pre>git config --global core.sshcommand '/mnt/c/Windows/System32/OpenSSH/ssh.exe'</pre></div></section></section>
<section class="doc-section level-1"><h2 id="_credential_settings">Credential settings</h2><section class="doc-section level-2"><h3 id="_credential_helper"><code>credential.helper</code></h3><p>This is quite similar to <code>core.sshcommand</code> I mentioned above when using Git in WSL.
You can choose to use HTTPS over SSH when talking with Git remotes.
But WSL doesn’t have a built-in way to store these credentials,
and using plaintext file <code>.git-credentials</code> is not something you want to be doing.</p>
<p>Again, Windows comes to the rescue.
Here, you have to install Git on Windows
to be able to use its credential manager from WSL Git.
Then, you can authenticate once,
usually via a browser window,
and <em>Git will remember this</em>.</p>
<div class="listing-block"><pre>git config --global credential.helper '/mnt/c/Program\ Files/Git/mingw64/bin/git-credential-manager.exe'</pre></div>
<p>Make sure to quote the path
as space in <code>Program Files</code> would cause some issues otherwise.
And use the actual path of where you have Git for Windows installed,
other default alternative is <code>AppData</code> of your Windows user.</p></section></section>
<section class="doc-section level-1"><h2 id="_git_features">Git features</h2><section class="doc-section level-2"><h3 id="_feature_manyfiles"><code>feature.manyFiles</code></h3><p>Git introduced this feature a few years ago to improve support for bigger repos.
I naively enabled it after reading the change log
and it instantly broke some tools for me,
particularly Matlab Git integration was completely broken.
After these experiences I disabled this feature,
so my setting repeats the current default.<a class="footnote-ref" id="_footnoteref_5" href="proxy.php?url=#_footnote_5" title="View footnote 5" role="doc-noteref">[5]</a></p>
<div class="listing-block"><pre>git config --global feature.manyFiles false</pre></div>
<p>If you’re struggling with Git performance you might consider to enable it.
Hopefully after a few years tools already support it.</p></section></section>
<section class="doc-section level-1"><h2 id="_git_commit"><code>git commit</code></h2><section class="doc-section level-2"><h3 id="_commit_verbose"><code>commit.verbose</code></h3><p><code>git commit</code> has a <code>--verbose</code> flag
which includes committed diff in a commit window.
The commit windows is nothing more but an automatically opened file
which you edit, save, and quit,
which makes git do the commit.</p>
<p>This option always adds this flag,
so you can see what are you committing every time.</p>
<div class="listing-block"><pre>git config --global commit.verbose true</pre></div></section></section>
<section class="doc-section level-1"><h2 id="_git_fetch"><code>git fetch</code></h2><section class="doc-section level-2"><h3 id="_fetch_prune"><code>fetch.prune</code></h3><p>You can remove any remote-tracking references
(i.e. <code>origin/<branch-name></code>)
which were removed on the remote with <code>git fetch --prune</code>.
I find it very convenient to be the default,
as branches are usually removed after merging pull request,
so you can quickly have a lot of dangling references.</p>
<div class="listing-block"><pre>git config --global fetch.prune true</pre></div>
<p>If you have multiple remotes,
and would like to prune references from only some of them,
it’s also an option.
For that, there is another command:</p>
<div class="listing-block"><pre>git config --global remote.<name>.prune true</pre></div>
<p><code><name></code> can be anything,
and by convention <code>origin</code> is used as the default remote name.</p>
<p>You might be wondering if setting something like
<code>git config --global remote.prune true</code> is allowed.
Yes, it is, and it does pretty much the same thing
as setting <code>fetch.prune</code> to true.<a class="footnote-ref" id="_footnoteref_6" href="proxy.php?url=#_footnote_6" title="View footnote 6" role="doc-noteref">[6]</a></p></section></section>
<section class="doc-section level-1"><h2 id="_git_pull"><code>git pull</code></h2><section class="doc-section level-2"><h3 id="_pull_rebase"><code>pull.rebase</code></h3><p>I like
<a href="proxy.php?url=https://youtu.be/xN1-2p06Urc">explanation from this video</a>
why default behavior of <code>git pull</code> is a bit annoying.
Its title is a little bit clickbaitish,
but I agree with the arguments.</p>
<p>I was also a bit surprised
that author didn’t mentioned option we’re talking about here.
To do <code>git pull --rebase</code> automatically with each pull set:</p>
<div class="listing-block"><pre>git config --global pull.rebase true</pre></div></section>
<section class="doc-section level-2"><h3 id="_pull_ff_only"><code>pull.ff only</code></h3><p>There is also another option
which enforces <a href="proxy.php?url=https://graphite.dev/guides/git-config-ff-only">fast-forward merge in git pull</a>.
The difference is that <code>--ff-only</code> merge works
only if there are no newer commits coming from the remote.
Otherwise it fails,
and you have to do something with it manually.
That means you are forced to do the rebase on your own.</p>
<p>This adds you more work
but one can advocate that it’s safer,
as you have to explicitly rebase to the new version.</p>
<p>This is in line with the approach <strong>never use git pull</strong>
(i.e. <strong>use git fetch and git rebase instead</strong>).
If you’re into that,
consider using this option.</p></section></section>
<section class="doc-section level-1"><h2 id="_git_push"><code>git push</code></h2><p>The biggest problem with this command is people overusing <code>--force</code>. There is a
safer replacement for it.</p>
<p>You can use <code>--force-with-lease</code> instead of <code>--force</code> to only overwrite changes
you’re expecting. Either the ones you have locally, or ones you fetched before.
If there will be any new commit on a remote, force push with lease fails.</p></section>
<section class="doc-section level-1"><h2 id="_git_merge"><code>git merge</code></h2><section class="doc-section level-2"><h3 id="_merge_conflictstyle"><code>merge.conflictstyle</code></h3><p>I think I don’t know a person who likes to solve merge conflicts.
But I noticed that many people are more scared about them
then they probably should be.</p>
<p>Setting a better conflict style in Git
is one of the steps to make them less scary.</p>
<p><code>zdiff3</code> is a relatively new conflict style in Git
which was introduced in 2022 with version 2.35.
That means that you still can work with systems
which don’t have this feature,
so make sure to see if your Git version is new enough.</p>
<p>But what is <code>zdiff3</code>?
It’s an updated version of <code>diff3</code>
which is much older algorithm<a class="footnote-ref" id="_footnoteref_7" href="proxy.php?url=#_footnote_7" title="View footnote 7" role="doc-noteref">[7]</a>
and which has one big benefit:
in classic conflict view you see two versions of the code,
<code>ours</code> and <code>theirs</code>, as Git labels it.
<code>diff3</code> also shows what was <em>before that</em>,
which we can name a common ancestor of both commits.<a class="footnote-ref" id="_footnoteref_8" href="proxy.php?url=#_footnote_8" title="View footnote 8" role="doc-noteref">[8]</a></p>
<p>This simple addition often adds much needed context,
to solve merge conflict faster, and do it correctly.</p>
<p>Unfortunately, <code>diff3</code> isn’t ideal,
and sometimes creates more noisy conflicts,
marking more lines as conflicting.<a class="footnote-ref" id="_footnoteref_9" href="proxy.php?url=#_footnote_9" title="View footnote 9" role="doc-noteref">[9]</a></p>
<p><code>zdiff3</code> addresses these issues.
Interestingly enough, <code>z</code> in the name comes from the word <em>zealous</em>.</p>
<div class="listing-block"><pre>git config --global merge.conflictstyle zdiff3</pre></div></section></section>
<section class="doc-section level-1"><h2 id="_git_init"><code>git init</code></h2><section class="doc-section level-2"><h3 id="_init_defaultbranch"><code>init.defaultBranch</code></h3><p>Like it or not, but <code>main</code> is the new <code>master</code> in Git,
and it doesn’t seem to be changing any time soon.</p>
<p>Git though still uses default <code>master</code>, so this option has to be set.</p>
<div class="listing-block"><pre>git config --global init.defaultBranch main</pre></div></section></section>
<section class="doc-section level-1"><h2 id="_git_aliases">Git aliases</h2><section class="doc-section level-2"><h3 id="_git_git"><code>git git</code></h3><p>I borrowed that idea from Anthony Sottile<a class="footnote-ref" id="_footnoteref_10" href="proxy.php?url=#_footnote_10" title="View footnote 10" role="doc-noteref">[10]</a>.
It allows you to type <code>git git <command></code>
and make the command still work.
In fact, you might type as many <code>git</code> commands as you like,
as it’s recursive.</p>
<p>Why? Same as Anthony,
I sometimes type <code>git</code> in the terminal,
then start thinking about something,
then and go back to typing,
often typing <code>git</code> again due to muscle memory.
Adding an alias is simpler than fighting with it, apparently 😛</p>
<div class="listing-block"><pre>git config --global alias.git '!git'</pre></div></section>
<section class="doc-section level-2"><h3 id="_git_l"><code>git l</code></h3><p>Quickly output oneline log for all branches.
Very handy to quickly orient around in the repo.
This is one of my most-used git commands.</p>
<div class="listing-block"><pre>git config --global alias.l 'log --graph --all --oneline'</pre></div></section>
<section class="doc-section level-2"><h3 id="_git_lg"><code>git lg</code></h3><p><code>git l</code> with a bit more details.
It uses more formatting and
I copied it from someone’s dotfiles years ago.</p>
<div class="listing-block"><pre>git config --global alias.lg "log --graph --all --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%cr)%C(reset) %C(white)%s%C(reset) %C(bold yellow)- %cn%C(reset)%C(bold red)%d%C(reset)' --abbrev-commit --date=relative"</pre></div></section>
<section class="doc-section level-2"><h3 id="_git_llg"><code>git llg</code></h3><p>Even more detailed log.
Also copied from that same person.</p>
<div class="listing-block"><pre>git config --global alias.llg "log --graph --all --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%cD%C(reset) %C(bold green)(%cr)%C(reset)%C(bold yellow)%d%C(reset)%n'' %C(white)%s%C(reset) %C(bold yellow)- %cn%C(reset)' --abbrev-commit"</pre></div></section>
<section class="doc-section level-2"><h3 id="_git_k"><code>git k</code></h3><p>You migth not know this,
but Git comes with a GUI called <code>gitk</code>.
It isn’t always installed (e.g. MacOS Git via homebrew),
but when you have it,
it might be handy in some cases.</p>
<p>I’m used to Git commits starting with <code>git <cmd></code>,
so I added an alias which reflects it.</p>
<div class="listing-block"><pre>git config --global alias.k '!gitk &'</pre></div></section>
<section class="doc-section level-2"><h3 id="_git_ka"><code>git ka</code></h3><p>Another <code>gitk</code> alias to run the program with <code>--all</code> flag
which then shows all branches.</p>
<div class="listing-block"><pre>git config --global alias.ka '!gitk --all &'</pre></div></section></section><section class="footnotes" aria-label="Footnotes" role="doc-endnotes"><hr><ol class="footnotes"><li class="footnote" id="_footnote_1" role="doc-endnote">I recommend a <a href="proxy.php?url=https://youtu.be/TtiBhktB4Qg">video from Scott Hanselman</a> if you want to learn more about this topic <a class="footnote-backref" href="proxy.php?url=#_footnoteref_1" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li><li class="footnote" id="_footnote_2" role="doc-endnote"><code>pre-commit</code> with its <a href="proxy.php?url=https://github.com/pre-commit/pre-commit-hooks/tree/429455474be018c8f085ae7d312432ab4154d5a2?tab=readme-ov-file#mixed-line-ending"><code>mixed-line-ending</code> hook</a> set to <code>lf</code> is a great tool for that <a class="footnote-backref" href="proxy.php?url=#_footnoteref_2" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li><li class="footnote" id="_footnote_3" role="doc-endnote"><code>pre-commit</code> mentioned above is a great way to do this. Also make sure to set up it to check it with each new commit or these <code>CRLF</code> files might start reappearing, as we live in a society <a class="footnote-backref" href="proxy.php?url=#_footnoteref_3" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li><li class="footnote" id="_footnote_4" role="doc-endnote">Refer to <a href="proxy.php?url=https://learn.microsoft.com/en-us/windows-server/administration/openssh/openssh_keymanagement">MS docs</a> on how to enable SSH agent <a class="footnote-backref" href="proxy.php?url=#_footnoteref_4" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li><li class="footnote" id="_footnote_5" role="doc-endnote">When writing this post, I did some research, and it seems that this might be due to <code>libgit2</code> incomparability with <code>manyFiles</code>. More details <a href="proxy.php?url=https://stackoverflow.com/a/77290211/14458327">on Stack Overflow</a> <a class="footnote-backref" href="proxy.php?url=#_footnoteref_5" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li><li class="footnote" id="_footnote_6" role="doc-endnote">There is quite a good summary of the topic <a href="proxy.php?url=https://stackoverflow.com/a/40842589">again on SO</a> <a class="footnote-backref" href="proxy.php?url=#_footnoteref_6" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li><li class="footnote" id="_footnote_7" role="doc-endnote">Introduced in 2007 with Git 1.5.0 <a class="footnote-backref" href="proxy.php?url=#_footnoteref_7" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li><li class="footnote" id="_footnote_8" role="doc-endnote">A pretty nice explanation of that is <a href="proxy.php?url=https://blog.nilbus.com/take-the-pain-out-of-git-conflict-resolution-use-diff3">in this blog post</a> <a class="footnote-backref" href="proxy.php?url=#_footnoteref_8" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li><li class="footnote" id="_footnote_9" role="doc-endnote">Again, <a href="proxy.php?url=https://stackoverflow.com/a/71254097/14458327">SO for the rescue</a> <a class="footnote-backref" href="proxy.php?url=#_footnoteref_9" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li><li class="footnote" id="_footnote_10" role="doc-endnote">Here’s his video about it: <a class="bare" href="proxy.php?url=https://youtu.be/BkUW2NgfZao">https://youtu.be/BkUW2NgfZao</a> <a class="footnote-backref" href="proxy.php?url=#_footnoteref_10" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li></ol></section>Convert dvdsub into srt subtitles2025-05-30T22:56:00+02:002025-05-30T22:56:00+02:00tpwotag:tpwo.github.io,2025-05-30:/blog/2025/05/30/convert-dvdsub-into-srt-subtitlesHow to convert bitmap subtitles into text format<section id="preamble" aria-label="Preamble"><p>I wanted to extract subtitles from a bunch of <code>.mp4</code> files,
as they were containing song names playing in the background of the movie.</p>
<p>This was more complicated than I thought.</p>
<p>It turns out video subtitles can come in very different formats.
One of them is <code>dvdsub</code>, and it’s a bitmap subtitle format.</p>
<p>To convert it into something you can grep,
you have to go through a lot of steps including OCR.</p>
<p>Fortunately, good people created tools to do just that.</p></section>
<section class="doc-section level-1"><h2 id="_ffmpeg_for_the_win">ffmpeg for the win</h2><p>I loved <code>ffmpeg</code> once I discovered it many years ago.
I really don’t like bloated constrained GUI-based apps
to manipulate video and audio files.</p>
<p>It can help with subtitles as well.</p>
<p>To install on MacOS you can use homebrew:</p>
<div class="listing-block"><pre>brew install ffmpeg</pre></div>
<p>On Linux it should be available in your package manager.
On Windows probably the best solution is to use
<a href="proxy.php?url=https://learn.microsoft.com/en-us/windows/wsl/install">WSL2</a>.</p></section>
<section class="doc-section level-1"><h2 id="_check_your_subs">Check your subs</h2><p><code>ffmpeg</code> can help you verifying that your subs are actually in <code>dvdsub</code> format.
Use <code>-i</code> and point to the video file:</p>
<div class="listing-block"><pre>ffmpeg -i <video-file></pre></div>
<p>e.g. <code>ffmpeg -i video.mp4</code></p>
<p>Output should look like this:</p>
<div class="listing-block"><pre><...snip...>
Stream #0:0[0x1](eng): Video: mpeg4 (Simple Profile) (mp4v / 0x7634706D), yuv420p, 448x336 [SAR 1:1 DAR 4:3], 825 kb/s, 29.97 fps, 29.97 tbr, 30k tbn (default)
Metadata:
creation_time : 2007-09-27T17:47:49.000000Z
handler_name : Video Media Handler
vendor_id : [0][0][0][0]
Stream #0:1[0x2](eng): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 74 kb/s (default)
Metadata:
creation_time : 2007-09-27T17:47:49.000000Z
handler_name : Sound Media Handler
vendor_id : [0][0][0][0]
Stream #0:2[0x3](eng): Subtitle: dvd_subtitle (dvdsub) (mp4s / 0x7334706D), 720x480, 0 kb/s (default)
Metadata:
creation_time : 2007-09-27T17:47:57.000000Z
handler_name : Unspecified
At least one output file must be specified</pre></div>
<p>The part we’re interested in is
<code>Subtitle: dvd_subtitle (dvdsub)</code>.
This confirms that our subtitles are bitmap-based,
and are one of the three streams in the file.</p>
<p>Indexing is 0-based with a number after <code>Stream #</code>,
i.e. <code>0:2</code> in case of subtitle stream.</p>
<p>Stream index is required in the next step.</p></section>
<section class="doc-section level-1"><h2 id="_extract_subs_into_separate_container">Extract subs into separate container</h2><p><code>ffmpeg</code> comes in handy again:</p>
<div class="listing-block"><pre>ffmpeg -i <video-file> -map <stream-index> -c:s copy <output-file>.mkv</pre></div>
<p>e.g.</p>
<div class="listing-block"><pre>ffmpeg -i video.mp4 -map 0:2 -c:s copy output.mkv</pre></div></section>
<section class="doc-section level-1"><h2 id="_convert_mkv_into_sub_and_idx_files">Convert <code>mkv</code> into <code>sub</code> and <code>idx</code> files</h2><p>This step requires mkvextract.
Again, you can install it with homebrew:</p>
<div class="listing-block"><pre>brew install mkvtoolnix</pre></div>
<p>Then <code>mkvextract</code> should be available.
You can use it to create <code>sub</code> and <code>idx</code> file from <code>mkv</code>:</p>
<div class="listing-block"><pre>mkvextract tracks <output-file>.mkv 0:<output-file>.sub</pre></div>
<p>e.g. <code>mkvextract tracks output.mkv 0:output.sub</code></p>
<p>We do it because OCR tools support this format instead of <code>mkv</code>.</p></section>
<section class="doc-section level-1"><h2 id="_use_ocr_to_get_srt_file">Use OCR to get <code>srt</code> file</h2><p>This step took me the most, as the tool we’re using here is pretty old,
and I had to manually clone repo and tweak it to be able to compile it.</p>
<section class="doc-section level-2"><h3 id="_install_vobsub2srt">Install <code>VobSub2SRT</code></h3><p>You can use my fork, so you don’t have to apply the fix by yourself.
The "fix" was bumping the minimum required version of <code>cmake</code> which I did in
<a href="proxy.php?url=https://github.com/tpwo/VobSub2SRT/commit/cd391e43cfc13df9fc54a58246664bb3c42b481a">this one-liner</a>.</p>
<section class="doc-section level-3"><h4 id="_use_my_homebrew_tap">Use my homebrew tap</h4><aside class="admonition-block note" role="note"><h6 class="block-title label-only"><span class="title-label">Note: </span></h6><p>Remember that you probably shouldn’t just trust me
and run these commands below without checking the source code first 😉</p></aside>
<p>I was able to set up a private homebrew tap,
so it’s possible to install my fixed version with these two commands.
Homebrew will take care of build dependencies
(or at least for <code>cmake</code> and <code>tesseract</code>):</p>
<div class="listing-block"><pre>brew tap tpwo/vobsub2srt
brew install --HEAD tpwo/vobsub2srt/vobsub2srt</pre></div>
<p>Setting this up was new for me.
It turns out that Homebrew requires
<code><user>/homebrew-<name></code> repo to be present,
so I <a href="proxy.php?url=https://github.com/tpwo/homebrew-vobsub2srt">created it</a>.
This repo contains the
<a href="proxy.php?url=https://github.com/tpwo/homebrew-vobsub2srt/blob/main/Formula/vobsub2srt.rb">formula</a>
to install VobSub2SRT
from <a href="proxy.php?url=https://github.com/tpwo/VobSub2SRT">my fork</a>.</p></section>
<section class="doc-section level-3"><h4 id="_manual_installation">Manual installation</h4><p>If you prefer manual installation, it’s also possible.
Before compilation, you have to install build dependencies:</p>
<div class="listing-block"><pre>brew install cmake
brew install tesseract</pre></div>
<p>Then you can clone my fork and compile the tool:</p>
<div class="listing-block"><pre class="rouge highlight"><code data-lang="bash">git clone https://github.com/tpwo/VobSub2SRT
<span class="nb">cd </span>VobSub2SRT
<span class="c"># Then you can follow original README</span>
./configure
make
<span class="c"># `vobsub2srt` should be present in `build/bin`</span></code></pre></div>
<p>Original README suggests <code>sudo make install</code>,
but I just grabbed the compiled binary and moved it to my PATH in <code>~/.local/bin</code>.</p>
<p>Then you can finally convert <code>sub</code> and <code>idx</code> file into <code>srt</code> file:</p>
<div class="listing-block"><pre># Note we don't pass file extension here
vobsub2srt <output-file></pre></div>
<p>e.g. <code>vobsub2srt output</code></p></section></section></section>
<section class="doc-section level-1"><h2 id="_script_to_run_it_all_with_one_command">Script to run it all with one command</h2><p>I had a bunch videos with embedded subtitles,
so I created a Bash script to quickly run it with <code>for f in *</code>:</p>
<div class="listing-block"><pre class="rouge highlight"><code data-lang="bash"><span class="nv">title</span><span class="o">=</span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span>
<span class="nv">title_wo_suffix</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">title</span><span class="p">%.*</span><span class="k">}</span><span class="s2">"</span>
<span class="nv">outdir</span><span class="o">=</span>music
<span class="nv">stream</span><span class="o">=</span><span class="si">$(</span>ffmpeg <span class="nt">-i</span> <span class="s2">"</span><span class="nv">$title</span><span class="s2">"</span> 2>&1 | <span class="nb">grep</span> <span class="s1">'dvdsub'</span> | <span class="nb">awk</span> <span class="nt">-F</span> <span class="s1">'[#\[]'</span> <span class="s1">'{print $2}'</span><span class="si">)</span>
<span class="k">if</span> <span class="o">[[</span> <span class="nt">-z</span> <span class="s2">"</span><span class="nv">$stream</span><span class="s2">"</span> <span class="o">]]</span><span class="p">;</span> <span class="k">then
</span><span class="nb">echo</span> <span class="s1">'No subtitles track in the video!'</span>
<span class="nb">exit </span>1
<span class="k">fi
</span><span class="nb">mkdir</span> <span class="nt">-p</span> <span class="s2">"</span><span class="nv">$outdir</span><span class="s2">"</span>
ffmpeg <span class="nt">-i</span> <span class="s2">"</span><span class="nv">$title</span><span class="s2">"</span> <span class="nt">-map</span> <span class="s2">"</span><span class="nv">$stream</span><span class="s2">"</span> <span class="nt">-c</span>:s copy <span class="s2">"</span><span class="nv">$outdir</span><span class="s2">"</span>/<span class="s2">"</span><span class="nv">$title_wo_suffix</span><span class="s2">"</span>.mkv
mkvextract tracks <span class="s2">"</span><span class="nv">$outdir</span><span class="s2">"</span>/<span class="s2">"</span><span class="nv">$title_wo_suffix</span><span class="s2">"</span>.mkv 0:<span class="s2">"</span><span class="nv">$outdir</span><span class="s2">"</span>/<span class="s2">"</span><span class="nv">$title_wo_suffix</span><span class="s2">"</span>.sub
vobsub2srt <span class="s2">"</span><span class="nv">$outdir</span><span class="s2">"</span>/<span class="s2">"</span><span class="nv">$title_wo_suffix</span><span class="s2">"</span></code></pre></div>
<p>To run it, save it under <code><name></code>,
do <code>chmod +x <name></code>,
and run:</p>
<div class="listing-block"><pre>./<name> <video-file>.<ext></pre></div>
<p>e.g. <code>./extract-subs video.mp4</code>.</p>
<p>To run it for e.g. all <code>.mp4</code> files in the current folder do:</p>
<div class="listing-block"><pre>for f in *.mp4; do ./extract-subs.sh "$f"; done</pre></div></section>
<section class="doc-section level-1"><h2 id="_sources_and_further_reading">Sources and further reading</h2><div class="ulist"><ul><li>Original VobSub2SRT repo (I forked a fork!):<br>
<a class="bare" href="proxy.php?url=https://github.com/ruediger/VobSub2SRT">https://github.com/ruediger/VobSub2SRT</a></li><li>Tutorial describing alternative flow with <code>ffmes</code>. I figured VobSub2SRT is easier to run on Apple Sillcon Macbook:<br>
<a class="bare" href="proxy.php?url=https://subarashii-no-fansub.github.io/Subbing-Tutorial/Convert-vobsub-file-into-srt/">https://subarashii-no-fansub.github.io/Subbing-Tutorial/Convert-vobsub-file-into-srt/</a></li></ul></div></section>Use SSH key to sign your Git commits2025-05-30T21:33:00+02:002025-06-16T21:52:00+02:00tpwotag:tpwo.github.io,2025-05-30:/blog/2025/05/30/use-ssh-key-to-sign-your-git-commitsHow to sign commits without using GPG<section class="doc-section level-1"><h2 id="_but_why">But why?</h2><p>You might wonder why should you sign your commits.
This is a valid question, and some people just don’t care about it.
If you don’t have a ready opinion<a class="footnote-ref" id="_footnoteref_1" href="proxy.php?url=#_footnote_1" title="View footnote 1" role="doc-noteref">[1]</a>, I’ll try to convince you.</p>
<section class="doc-section level-2"><h3 id="_choose_your_warrior_committer">Choose your <s>warrior</s> committer</h3><aside class="admonition-block note" role="note"><h6 class="block-title label-only"><span class="title-label">Note: </span></h6><p>Yes, I know that author and committer are two different things in Git,
but you can fabricate both of them pretty much the same way.</p></aside>
<p>In Git you can assign any author to any commit.
This is a basic functionality of Git,
you can put any name and email in <code>.gitconfig</code>
or pass one-time name/email with <code>-C</code> when committing
(or set ENV variables etc. – there are many ways).</p></section>
<section class="doc-section level-2"><h3 id="_make_github_to_link_their_profile">Make GitHub to link their profile</h3><p>Then, you can push this commit to GitHub.
If the commit email address is already used by someone on GitHub,
GitHub links it with their profile.
Even if this is obviously not their commit.
GitHub cannot know this.</p></section>
<section class="doc-section level-2"><h3 id="_fork_their_repo_and_display_fabricated_commit_from_the_base_url">Fork their repo and display fabricated commit from the base URL</h3><p>You might think that no one cares about you
doing some fake commits in your repos.
But you can fork any repo and fake commits in the fork.</p>
<p>Then, you can display a commit from fork
even when looking at it from the original repo.
Actually, this is a big problem with GitHub UI,
as it treats all forks to be connected with the base repo.</p>
<p>I once found a fabricated Linus Torvalds commit
in which he deleted the whole
<a href="proxy.php?url=https://github.com/torvalds/linux">Linux codebase</a>
with a funny commit message.</p>
<p>Unfortunately, I haven’t found it now but
<a href="proxy.php?url=https://github.com/Amog-OS/AmogOS/commit/4f503a0">here’s something similar</a>
(also from Torvalds!).</p>
<p>TLDR: fabrication process is simple:</p>
<div class="olist arabic"><ol class="arabic"><li>Fork a repo</li><li>Create a fake commit.</li><li>Display it in context of the original repo. <strong>GitHub allows for that!</strong></li></ol></div></section>
<section class="doc-section level-2"><h3 id="_what_can_i_do">What can I do?</h3><p>You cannot prevent people from using your (public) email
and fabricate commits on your account.
But you can turn on GitHub <strong>vigilant mode</strong> which is described in
<a href="proxy.php?url=https://docs.github.com/en/authentication/managing-commit-signature-verification/displaying-verification-statuses-for-all-of-your-commits">their docs</a>.</p>
<p>Then all of your unsigned commits will be marked explicitly as unverified.
Unfortunately, also all of <strong>your own</strong> commits,
prior you started to sign them, will be marked as well.</p></section></section>
<section class="doc-section level-1"><h2 id="_old_times">Old times</h2><p>Nevermind, let’s go back to the original topic.
When you wanted to sign your commits in Git a few years ago,
you were forced to use <a href="proxy.php?url=https://en.wikipedia.org/wiki/GNU_Privacy_Guard">GPG</a>.
Unless you already have and use GPG,
it was a lot of pain.
GPG is quite old, and it doesn’t offer the best user experience.
Using it, meant maintaining another key,
which you have to protect with another strong password and back it up.
This was enough to stop many people from signing their commits.</p></section>
<section class="doc-section level-1"><h2 id="_alternative_to_gpg">Alternative to GPG</h2><p>By the end of 2021 with v2.34.0
Git added a way to sign your commits using SSH keys.
I tried switching to SSH a few years ago but it didn’t work,
and quick research showed that I’m not the only one.</p>
<p>Out of curiosity I checked it now,
and I was able to sign my commits after a few minutes of configuration,
and this post is the result of that.
Time heals wounds!</p></section>
<section class="doc-section level-1"><h2 id="_configure_git">Configure Git</h2><p>I assume that you already have and use a password-protected SSH key<a class="footnote-ref" id="_footnoteref_2" href="proxy.php?url=#_footnote_2" title="View footnote 2" role="doc-noteref">[2]</a>.
Then you can tell Git to use it:</p>
<div class="listing-block"><pre>git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/<your-public-key></pre></div>
<p>And that’s all!
To sign a single commit:</p>
<aside class="admonition-block note" role="note"><h6 class="block-title label-only"><span class="title-label">Note: </span></h6><p>while doing research you might also encounter sign-off
which is a different thing in Git, and is not backed by by any encryption<a class="footnote-ref" id="_footnoteref_3" href="proxy.php?url=#_footnote_3" title="View footnote 3" role="doc-noteref">[3]</a>.</p></aside>
<div class="listing-block"><pre>git commit -S -m 'Your commit message'</pre></div>
<p>To always sign your commits:</p>
<div class="listing-block"><pre>git config --global commit.gpgsign true</pre></div>
<p>To also always sign your tags:</p>
<div class="listing-block"><pre>git config --global tag.gpgsign true</pre></div></section>
<section class="doc-section level-1"><h2 id="_configure_github">Configure GitHub</h2><p>To have commits marked as signed in GitHub,
you have to add SSH key one more time,
and choose it to be your <strong>Signing Key</strong>:</p>
<div class="image-block"><img src="proxy.php?url=https://tpwo.github.io/images/use-ssh-key-to-sign-your-git-commits/add-new-ssh-key.png" alt="Add new SSH signing key in GitHub"></div>
<p>After doing that,
your signed commits will be marked as <strong>Verified</strong>:</p>
<div class="image-block"><img src="proxy.php?url=https://tpwo.github.io/images/use-ssh-key-to-sign-your-git-commits/commit-sign.png" alt="Commit signed with SSH key as displayed in GitHub"></div></section>
<section class="doc-section level-1"><h2 id="_local_signers_file">Local signers file</h2><p>You can also create an allowed signers file
for quick local verification of signatures</p>
<p>Create a file (e.g., <code>~/.config/git/allowed_signers</code>)
listing trusted email and public key pairs in this format:</p>
<div class="listing-block"><pre>[email protected] ssh-ed25519 AAAAC3NzaC1...</pre></div>
<p>Then tell Git to use it:</p>
<div class="listing-block"><pre>git config --global gpg.ssh.allowedSignersFile '~/.config/git/allowed_signers'</pre></div>
<p>You can now verify if your commit was signed properly:</p>
<div class="listing-block"><pre>$ git verify-commit <hash>
Good "git" signature for [email protected] with ED25519 key SHA256:<...snip...></pre></div>
<p>You can do it for git log as well:</p>
<div class="listing-block"><pre>$ git log --show-signature
commit <hash> (HEAD -> main, origin/main)
Good "git" signature for [email protected] with ED25519 key SHA256:<...snip...>
Author: John Doe <[email protected]>
Date: Fri May 30 21:22:36 2025 +0200
Hello there</pre></div></section>
<section class="doc-section level-1"><h2 id="_further_reading">Further reading</h2><div class="ulist"><ul><li>How to do it on Windows:<br>
<a class="bare" href="proxy.php?url=https://www.meziantou.net/signing-commits-in-git-using-ssh-keys-on-windows.htm">https://www.meziantou.net/signing-commits-in-git-using-ssh-keys-on-windows.htm</a></li></ul></div></section><section class="footnotes" aria-label="Footnotes" role="doc-endnotes"><hr><ol class="footnotes"><li class="footnote" id="_footnote_1" role="doc-endnote">I don’t want to go into details, but e.g. <a href="proxy.php?url=https://withblue.ink/2020/05/17/how-and-why-to-sign-git-commits.html">this blog post</a> does it very well <a class="footnote-backref" href="proxy.php?url=#_footnoteref_1" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li><li class="footnote" id="_footnote_2" role="doc-endnote">If you don’t have your SSH key set up, <a href="proxy.php?url=https://www.git-tower.com/blog/setting-up-ssh-for-commit-signing">this blog post</a> will be helpful. They go into much more details along with tips how to load SSH keys into keychain on MacOS etc. <a class="footnote-backref" href="proxy.php?url=#_footnoteref_2" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li><li class="footnote" id="_footnote_3" role="doc-endnote"><a href="proxy.php?url=https://youtu.be/6hu3cbBhHqQ">This video</a> shows different ways to sign a Git commit. Also, author states that he doesn’t sign his commits, so you clearly see that opinions vary on this topic. <a class="footnote-backref" href="proxy.php?url=#_footnoteref_3" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li></ol></section>Clipboard sync between WSL Neovim and Windows2024-09-17T21:52:00+02:002024-09-17T21:52:00+02:00tpwotag:tpwo.github.io,2024-09-17:/blog/2024/09/17/clipboard-sync-between-wsl-neovim-and-windows<section id="preamble" aria-label="Preamble"><p>I recently started migration from Vim to Neovim.
One of the first issues was proper configuration of clipboard.
By <em>proper …</em></p></section><section id="preamble" aria-label="Preamble"><p>I recently started migration from Vim to Neovim.
One of the first issues was proper configuration of clipboard.
By <em>proper</em>, I mean a situation
where I have a working 2-way-sync clipboard
between Neovim running in tmux in WSL in Windows Terminal and Windows,
so I can easily copy and paste
from my browser or any other program to and from Neovim.</p>
<p>With Vim I intially achieved that with <a href="proxy.php?url=https://github.com/Opticos/GWSL-Source">GWSL</a>
and configured <code>$DISPLAY</code> env variable
which was a quite complex and unstable solution
(GWSL sometimes has weird issues).
Thankfully, it was needed only on older versions of Windows 10
(my <code>$CURRENT_COMPANY</code> still uses the old Win10 version though…​).
Up-to-date Windows 10 and Windows 11
support clipboard sync for Vim out of the box.
They have something like GWSL built-in,
and surprisingly it’s quite stable and just works.
Bravo Microsoft 😛</p>
<p>The only caveat is to install Vim version which has clipboard support – usually <code>vim-gtk</code> is recommended on Debian/Ubuntu.</p></section>
<section class="doc-section level-1"><h2 id="_the_unexpected">The unexpected</h2><p>I was a bit surprised to find out
that this doesn’t work with Neovim.</p>
<p>Some googling brought the solution: <code>win32yank</code>.
This is a small Rust program
which integrates with Windows cliboard,
providing an interface which can be easily used by Neovim.</p>
<p>Actually, Neovim has built-in support for it.
You just need to make sure that <code>win32yank.exe</code> is on your <code>$PATH</code>.</p></section>
<section class="doc-section level-1"><h2 id="_how_to_get_win32yank_exe">How to get <code>win32yank.exe</code>?</h2><aside class="admonition-block note" role="note"><h6 class="block-title label-only"><span class="title-label">Note: </span></h6><p>If, from any reason, you don’t want Neovim on Windows,
you can download only the <code>win32yank.exe</code> file.
E.g. by doing <code>winget install win32yank</code>.
Just make sure to use the correct path to the <code>exe</code> file.</p></aside>
<p>I use WSL to do most of my text editting,
but I still find it useful to have the same editor on Windows itself.</p>
<p>You can install it most easily with
<a href="proxy.php?url=https://learn.microsoft.com/en-us/windows/package-manager/winget/">winget</a>,
i.e. <code>winget install Neovim.Neovim</code>.</p>
<p>Why this it important?
It turns out that Neovim on Windows is shipped with <code>win32yank</code>!</p></section>
<section class="doc-section level-1"><h2 id="_creating_symlink">Creating symlink</h2><aside class="admonition-block note" role="note"><h6 class="block-title label-only"><span class="title-label">Note: </span></h6><p>I noticed that Neovim has issues
if I copy the Windows exectuable directly to WSL drive.
That’s why you probably should create a symlink,
even if you download only the binary.</p></aside>
<p><code>winget</code> installs Neovim in <code>Program Files</code>,
and <code>win32yank.exe</code> is located in the <code>bin</code> subfolder.
Use this command to create a symlink in your home folder,
so Neovim can see it.</p>
<div class="listing-block"><pre>ln --symbolic /mnt/c/Program\ Files/Neovim/bin/win32yank.exe ~/.local/bin/win32yank.exe</pre></div>
<p>Of course, <code>~/.local/bin</code> has to exist
and be in your <code>$PATH</code> for this to work.</p></section>Playing Super Mario World in 20242024-08-12T23:12:00+02:002025-06-17T01:04:00+02:00tpwotag:tpwo.github.io,2024-08-12:/blog/2024/08/12/playing-super-mario-world-in-2024How to run it on modern platforms in widescreen mode<section id="preamble" aria-label="Preamble"><div class="quote-block"><blockquote><p>Thank you Mario! But our princess is in another castle!</p><footer>— <cite><a href="proxy.php?url=https://en.wikipedia.org/wiki/Our_princess_is_in_another_castle!">Toad</a></cite></footer></blockquote></div>
<p><a href="proxy.php?url=https://en.wikipedia.org/wiki/Super_Mario_World">Super Mario World</a>
is one of my childhood games I remember really well.
It was released in 1990 but recently I found out
that we can still play it today on modern platforms
with widescreen support enabled!</p>
<figure class="image-block"><img src="proxy.php?url=https://tpwo.github.io/images/playing-super-mario-world-in-2024/cover.png" alt="Super Mario World Cover">
<figcaption>Cover of the US version of Super Mario World</figcaption></figure>
<p>The base to do it is
<a href="proxy.php?url=https://github.com/bsnes-emu/bsnes">bsnes</a>,
a multi-platform SNES emulator.
There is a fork of it
which supports HD video features called
<a href="proxy.php?url=https://github.com/DerKoun/bsnes-hd">bsnes-hd</a>,
and we’ll be using it.</p>
<p>Super Mario World Widescreen project is a fan-made patch
which can be found in
<a href="proxy.php?url=https://github.com/VitorVilela7/wide-snes">wide-snes</a>
GitHub repo.</p>
<p>We also need a patcher.
According to
<a href="proxy.php?url=https://sneslab.net/wiki/How_to_apply_ROM_patches">sneslab.net manual</a>
we can use either FLIPS or beam.
They host FLIPS directly on their site:
<a class="bare" href="proxy.php?url=https://sneslab.net/tools/floating.zip">https://sneslab.net/tools/floating.zip</a>.</p>
<p>Now, the only missing piece is ROM with the original game.
I found a copy of it on
<a href="proxy.php?url=https://archive.org/details/super-mario-world-usa_202411">archive.org</a>
which seems to be a pretty reasonable source.
If it’s there,
Nintendo probably doesn’t have anything against it,
or at least no longer cares.</p>
<aside class="admonition-block note" role="note"><h6 class="block-title label-only"><span class="title-label">Note: </span></h6><p>Internet Archive hosts multiple files under the linked project,
but we care only about a zip which contains the <code>sfc</code> file with the actual game.</p></aside></section>
<section class="doc-section level-1"><h2 id="_patching_the_rom">Patching the ROM</h2><p>After downloading the files,
you should have something like this
(we’re skipping emulator for now):</p>
<div class="listing-block"><pre>├── smw-widescreen.bps <b class="conum">1</b>
├── Super Mario World (USA).sfc <b class="conum">2</b>
└── floating
├── boring.zip
├── flips-linux
├── flips.exe <b class="conum">3</b>
├── license.txt
└── src.zip</pre><ol class="callout-list arabic"><li>Patch file</li><li>ROM file</li><li>FLIPS patcher</li></ol></div>
<p>Run FLIPS <3> and click <code>Apply Patch</code>.
At first it asks you for the patch file,
then for the ROM file,
and then for the filename to save the patched ROM.</p>
<div class="image-block left"><img src="proxy.php?url=https://tpwo.github.io/images/playing-super-mario-world-in-2024/flips.png" alt="FLIPS patcher"></div>
<p>You can notice that the patched file is twice the size of the original:</p>
<div class="listing-block"><pre class="rouge highlight"><code data-lang="shell"><span class="nv">$ </span><span class="nb">du</span> <span class="nt">-sh</span> <span class="k">*</span>
1.0M Super Mario World <span class="o">(</span>USA<span class="o">)</span> Widescreen Project.sfc
512K Super Mario World <span class="o">(</span>USA<span class="o">)</span>.sfc</code></pre></div></section>
<section class="doc-section level-1"><h2 id="_creating_widescreen_configuration_file">Creating widescreen configuration file</h2><p><code>bsnes-hd</code> requires a <code>bso</code> configuration file
in order to correctly run the game in a widescreen mode.
Thankfully, you don’t have to download it,
as the file is very short and simple,
and you can create it with this command:</p>
<div class="listing-block"><pre class="rouge highlight"><code data-lang="shell"><span class="nb">echo</span> <span class="s1">'w1s1W48S2i0o1p1b1B1c1'</span> <span class="o">></span> <span class="s1">'Super Mario World (USA) Widescreen Project.bso'</span></code></pre></div>
<p>The content of the file is taken from
<a href="proxy.php?url=https://github.com/VitorVilela7/wide-snes/blob/master/smw-widescreen.bso">wide-snes GitHub repo</a>.
I guess it uses special quirky syntax to tell the emulator
how it should behave when dealing with the patched ROM.</p></section>
<section class="doc-section level-1"><h2 id="_running_the_game">Running the game</h2><p>Now, you can unzip <code>bsnes-hd</code> and run it.
To make the game look better,
<strong>make sure to select</strong> <code>Settings > Shader > None</code>.
By default it’s set to <code>Blur</code> which makes the game very…​ well, blurry.
You can later switch these options on the fly,
maybe you’ll like it.</p>
<p>To start the game, either select <code>System > Load Game</code> or drag
and drop patched <code>sfc</code> file into the window.
Super Mario World should start in widescreen mode!</p>
<div class="image-block"><img src="proxy.php?url=https://tpwo.github.io/images/playing-super-mario-world-in-2024/gameplay.png" alt="Super Mario World in widescreen mode"></div></section>
<section class="doc-section level-1"><h2 id="_configuring_controller">Configuring controller</h2><p>I found the game to be much more enjoyable while playing it with a game pad.
Fortunately, <code>bsnes</code> allows for that.
Select <code>Settings > Input</code> to configure mappings for your device.</p>
<p>I’m using Xbox 360 controller,
and it works like a charm.
Here are my settings:</p>
<div class="image-block"><img src="proxy.php?url=https://tpwo.github.io/images/playing-super-mario-world-in-2024/gamepad-mapping.png" alt="Input settings for Xbox 360 controller"></div>
<p>These are pretty much defaults,
as much as you can tell that about SNES → Xbox 360 conversion,
but I added another mapping for <code>X</code>
which is used to run faster in the game.</p></section>Python integration tests with pytest and docker compose2024-07-15T22:48:00+02:002024-07-15T22:48:00+02:00tpwotag:tpwo.github.io,2024-07-15:/blog/2024/07/15/python-integration-tests-with-pytest-and-docker-compose<section id="preamble" aria-label="Preamble"><p>I really like
<a href="proxy.php?url=https://docs.pytest.org/">pytest</a>.
It’s a great tool for writing unit tests in Python,
as it provides a lot …</p></section><section id="preamble" aria-label="Preamble"><p>I really like
<a href="proxy.php?url=https://docs.pytest.org/">pytest</a>.
It’s a great tool for writing unit tests in Python,
as it provides a lot of features out of the box.
Some of them are quite magical, but they can make your life much easier,
so at the end you need much less code.</p>
<p>Recently, I experimented with using pytest fixture
to create a simple integration test skeleton which integrates docker.</p></section>
<section class="doc-section level-1"><h2 id="_fixtures">Fixtures</h2><p>Fixtures in pytest are a way to request for something in the test code.
There are two basic types of fixtures.<a class="footnote-ref" id="_footnoteref_1" href="proxy.php?url=#_footnote_1" title="View footnote 1" role="doc-noteref">[1]</a></p>
<div class="ulist"><ul><li><code>return</code> fixtures which provide a certain value or object<ul><li>This means we execute some code before requesting a fixture</li></ul></li><li><code>yield</code> fixtures which provide a certain state<ul><li>This means we execute some code before <strong>and after</strong> requesting a fixture</li></ul></li></ul></div>
<p>In this case the latter is be needed,
as we want to perform the test in a state when docker container is running.
This way we can startup a docker container
and clean it up regardless of the test results.</p>
<p>I’m using <code>docker compose</code>, as it provides more flexibility
and it’s cleaner than CLI flags for pure <code>docker</code>.
I’d use it even when you have only a single container,
as overhead is minimal.</p></section>
<section class="doc-section level-1"><h2 id="_implementation">Implementation</h2><p>You can see the test file skeleton here.</p>
<div class="listing-block"><pre class="rouge highlight"><code data-lang="python"><span class="kn">import</span> <span class="n">shutil</span>
<span class="kn">import</span> <span class="n">subprocess</span>
<span class="kn">import</span> <span class="n">time</span>
<span class="kn">import</span> <span class="n">pytest</span>
<span class="k">def</span> <span class="nf">tested_function</span><span class="p">():</span>
<span class="bp">...</span>
<span class="nd">@pytest.fixture</span>
<span class="k">def</span> <span class="nf">docker_container</span><span class="p">():</span>
<span class="n">out</span> <span class="o">=</span> <span class="n">subprocess</span><span class="p">.</span><span class="nf">run</span><span class="p">(</span>
<span class="p">(</span><span class="sh">'</span><span class="s">docker</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">compose</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">--file</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">testing/docker-compose.yml</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">up</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">--detach</span><span class="sh">'</span><span class="p">),</span>
<span class="n">check</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
<span class="p">)</span>
<span class="n">time</span><span class="p">.</span><span class="nf">sleep</span><span class="p">(</span><span class="mi">10</span><span class="p">)</span>
<span class="k">yield</span> <span class="n">out</span>
<span class="n">subprocess</span><span class="p">.</span><span class="nf">run</span><span class="p">(</span>
<span class="p">(</span><span class="sh">'</span><span class="s">docker</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">compose</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">--file</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">testing/docker-compose.yml</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">down</span><span class="sh">'</span><span class="p">),</span>
<span class="n">check</span><span class="o">=</span><span class="bp">True</span><span class="p">,</span>
<span class="p">)</span>
<span class="k">def</span> <span class="nf">test_integration</span><span class="p">(</span><span class="n">docker_container</span><span class="p">):</span>
<span class="n">actual</span> <span class="o">=</span> <span class="nf">tested_function</span><span class="p">()</span>
<span class="n">expected</span> <span class="o">=</span> <span class="sh">'</span><span class="s"><expected output></span><span class="sh">'</span>
<span class="k">assert</span> <span class="n">actual</span> <span class="o">==</span> <span class="n">expected</span></code></pre></div>
<p>The flow is quite simple here:</p>
<div class="ulist"><ul><li><code>test_integration</code> function starts</li><li>It requests <code>docker_container</code>, so the fixture function starts</li><li>We reach <code>yield</code> in <code>docker_container</code> and go back to the test function</li><li><code>docker_container</code> value in <code>test_integration</code> is a return value of <code>subprocess.run</code> from the fixture<ul><li>I’m not using it in this example</li></ul></li><li>Test code is run up to the end</li><li>Either if any exception is thrown or if the test code finished,
we go back to the fixture and execute rest of the code<ul><li>In this case it’s <code>docker compose down</code></li></ul></li></ul></div>
<p>The biggest drawback of this approach is <code>time.sleep</code>,
as <code>out</code> is returned when <code>docker compose up</code> starts running,
and it doesn’t mean that the container is ready for the test.
Depending on your use case you would need less or even more time here.</p>
<p>A smarter approach would be getting the container notify the code
that it’s ready in an event-driven fashion.
The presented solution is currently enough for my use case,
even though it might be quite limiting
if we want to scale it and run multiple test cases.</p>
<p>But for my use case this was good enough,
so I stopped here,
and maybe I’ll expand it in the future.</p></section>
<section class="doc-section level-1"><h2 id="_integration">Integration</h2><p>By default pytest picks up all files which are named <code>test_*.py</code> or <code>*_test.py</code>.
As integration tests are quite longer than unit tests,
we don’t want them to be run each time along with unit tests.</p>
<p>I chose to rename the file with integration tests to <code>integration.py</code>
which I put in <code>tests</code> folder along with other test files.
This works fine on a small-scale project.
In a bigger project,
I’d probably create a separate folder in <code>tests</code>
or maybe create a new top level folder like <code>integration_tests</code>.</p>
<p>With the simple approach
running these tests is a simple <code>pytest tests/integration.py</code>,
which I put under <code>Makefile</code> target <code>integration-tests</code>.
This way I can type <code>make i</code> and let tab-completion do the rest for me.</p>
<p>In CI, I’m running them in the same job as unit tests, directly after them,
as I assume that it doesn’t make sense to run costly integration tests
if any of my unit tests is failing (even if GitHub pays for my CI 😛).</p>
<p>You can see the actual implementation in my <a href="proxy.php?url=https://github.com/tpwo/event-scrapper-srt/blob/1de160463e0c02c49efb6a932213cf75ea112a5e/tests/integration.py">event-scrapper-srt</a> project.</p></section><section class="footnotes" aria-label="Footnotes" role="doc-endnotes"><hr><ol class="footnotes"><li class="footnote" id="_footnote_1" role="doc-endnote">To understand how they work <a href="proxy.php?url=https://youtu.be/ScEQRKwUePI">here’s a great video from Anthony Sottile about it</a> <a class="footnote-backref" href="proxy.php?url=#_footnoteref_1" role="doc-backlink" title="Jump to the first occurrence in the text">↩</a></li></ol></section>Python debugging 101: pdb2024-06-19T20:12:00+02:002024-07-15T23:50:00+02:00tpwotag:tpwo.github.io,2024-06-19:/blog/2024/06/19/python-debugging-101-pdb<section id="preamble" aria-label="Preamble"><p><code>pdb</code> is a simple debugger that comes by default with Python.
There are many more advanced alternatives like <code>pudb</code> or …</p></section><section id="preamble" aria-label="Preamble"><p><code>pdb</code> is a simple debugger that comes by default with Python.
There are many more advanced alternatives like <code>pudb</code> or <code>ipdb</code>,
but <code>pdb</code> is always available out of the box which is a big plus.</p>
<p><code>breakpoint()</code> was introduced in Python 3.7.
Before that,
import of <code>pdb</code> and calling a function that creates a breakpoint was required.</p></section>
<section class="doc-section level-1"><h2 id="_pdb_commands"><code>pdb</code> commands</h2><div class="ulist"><ul><li><code>h</code> / <code>help</code> – display available commands</li><li><code>c</code> / <code>continue</code> – continue running the code until the end</li><li><code>q</code> / <code>quit</code> – quit program by raising <code>BdbQuit</code> exception</li></ul></div>
<section class="doc-section level-2"><h3 id="_l_list"><code>l</code> / <code>list</code></h3><p>Prints lines of code around the line that is <strong>about to be run</strong>
(marked with an arrow).
Pressing enter will list even more file.
<code>list .</code> will reset the view to the original
(i.e. some lines before and after a line with arrow).</p></section>
<section class="doc-section level-2"><h3 id="_ll"><code>ll</code></h3><p>Long list, prints more lines of code.
Pressing enter has no effect.
There is no support for dot from this reason.</p></section>
<section class="doc-section level-2"><h3 id="_w_where"><code>w</code> / <code>where</code></h3><p>Displays a call stack that led to the execution of the given code.</p></section>
<section class="doc-section level-2"><h3 id="_u_up_d_down"><code>u</code> / <code>up</code> & <code>d</code> / <code>down</code></h3><p>Goes up or down one stack frame.
An arrow showed with <code>where</code> is updated to account for the new position.
Also, <code>list</code> will print the code around the current stack frame.</p>
<p><code>up</code> and <code>down</code> accept step parameter,
e.g. <code>up 3</code> will go up by three stack frames.</p></section>
<section class="doc-section level-2"><h3 id="_p_pp"><code>p</code> & <code>pp</code></h3><p>For printing and pretty printing.
The latter is especially helpful when printing more complex data structures.</p></section>
<section class="doc-section level-2"><h3 id="_n_next"><code>n</code> / <code>next</code></h3><p>Goes to the next line.</p>
<p>Whenever, a debugger returns it prints <code>--Return--</code> to the screen,
and pauses right after the return value.
Pressing <code>n</code> again will go to the next function.</p></section>
<section class="doc-section level-2"><h3 id="_s_step"><code>s</code> / <code>step</code></h3><p>Goes inside a function call.</p></section>
<section class="doc-section level-2"><h3 id="_r_return"><code>r</code> / <code>return</code></h3><p>Goes directly to the return statement of the current function.
Useful to quickly exit functions that we are not interested in debugging.</p></section></section>
<section class="doc-section level-1"><h2 id="_summary">Summary</h2><p>There are a lot of more features that can be used,
e.g. creating breakpoints from <code>pdb</code>,
but above I described the basics that should be useful most of the time.</p>
<p>For conditional breakpoints you can use Python <code>if</code>
and hide breakpoint under a certain condition.</p>
<p>I really recommend
<a href="proxy.php?url=https://youtu.be/0LPuG825eAk">this video from Anthony Sottile</a>
if you want to learn more and see how to use <code>pdb</code> in practice.</p></section>Xtract Ze Files!2024-06-19T19:45:00+02:002024-06-19T19:45:00+02:00tpwotag:tpwo.github.io,2024-06-19:/blog/2024/06/19/xtract-ze-files<p>It’s a neat way to remember <code>tar</code> flags. <mark>X</mark>tract <mark>Z</mark>e <mark>F</mark>iles!</p>
<div class="listing-block"><pre class="rouge highlight"><code data-lang="bash"><span class="nb">tar</span> <span class="nt">-xzf</span> <myarchive>.tar.gz …</code></pre></div><p>It’s a neat way to remember <code>tar</code> flags. <mark>X</mark>tract <mark>Z</mark>e <mark>F</mark>iles!</p>
<div class="listing-block"><pre class="rouge highlight"><code data-lang="bash"><span class="nb">tar</span> <span class="nt">-xzf</span> <myarchive>.tar.gz</code></pre></div>Hello, World!2024-06-05T23:53:00+02:002024-07-15T22:51:00+02:00tpwotag:tpwo.github.io,2024-06-05:/blog/2024/06/05/hello-world<p>We’re live!
I had an idea to create a personal blog for quite some time,
and here it is …</p><p>We’re live!
I had an idea to create a personal blog for quite some time,
and here it is.</p>
<p>It’s still in a very WIP state,
as I connected all the wires to run it,
and didn’t have much time to focus on
personalization, themes and other nice-to-eyes features.
But at first, I’d like to add some content,
so I can see the impact of style changes on real posts.</p>