<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet href="/custom-pretty-feed-v3.xsl" type="text/xsl"?><rss version="2.0"><channel><title>Kristoffer Balintona — #VCS</title><description>Entries tagged with &quot;VCS&quot;</description><link>https://kristofferbalintona.me/</link><language>en-us</language><image><url>https://kristofferbalintona.me/favicon-rss.png</url><title>Kristoffer Balintona — #VCS</title><link>https://kristofferbalintona.me</link></image><item><title>Jujutsu (jj) VCS workflows and the convenience of its “operation log”</title><link>https://kristofferbalintona.me/posts/202503270835/</link><guid isPermaLink="true">https://kristofferbalintona.me/posts/202503270835/</guid><description>&lt;p&gt;Over the last month or two I’ve been trying out the relatively new &lt;a href=&quot;https://jj-vcs.github.io/jj/latest/&quot;&gt;jujutsu&lt;/a&gt; version control system (VCS) — and loving it. At first, I discovered it and read their &lt;a href=&quot;https://github.com/jj-vcs/jj&quot;&gt;GitHub README&lt;/a&gt; with curiosity but skepticism. Then I sought a few &lt;a href=&quot;https://www.youtube.com/results?search_query=what+is+jujutsu+vcs&quot;&gt;YouTube videos&lt;/a&gt;&lt;sup&gt;&lt;a href=&quot;#fn-1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; demonstrating jj in action, and &lt;a href=&quot;https://jj-vcs.github.io/jj/latest/&quot;&gt;a tutorial&lt;/a&gt; for a text-based reference. I became very interested. I initialized a jj repo in one of my small git projects and played around. Since then, I’ve migrated all of my personal repositories to jj, and use jj for practically everything.&lt;/p&gt;
&lt;p&gt;From the time I’ve spent with jj, I’ve felt myself sliding into some general workflows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A &lt;code&gt;jj squash&lt;/code&gt; workflow&lt;br /&gt;
In git, this would be a workflow where you make a commit and continually amend/extend that commit until its intended goal is completed.
&lt;ol&gt;
&lt;li&gt;Make progress toward a desired goal.&lt;/li&gt;
&lt;li&gt;Commit (&lt;code&gt;jj commit&lt;/code&gt;, or &lt;code&gt;jj new&lt;/code&gt; then &lt;code&gt;jj describe -m &quot;...&quot;&lt;/code&gt;) with a message describing this unit of work.&lt;/li&gt;
&lt;li&gt;Continue making changes.&lt;/li&gt;
&lt;li&gt;Squash (perhaps interactively: &lt;code&gt;jj squash -i&lt;/code&gt;&lt;sup&gt;&lt;a href=&quot;#fn-2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;) new changes until the desired goal is accomplished.&lt;/li&gt;
&lt;li&gt;Maybe change the description of the previous commit commit.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Work in the working copy
&lt;ol&gt;
&lt;li&gt;Envision a goal for a new commit. Express that into the description of the working copy commit: &lt;code&gt;jj describe -m &quot;...&quot;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Work until much of that goal is met.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jj new&lt;/code&gt; to work on the next goal. Or &lt;code&gt;jj split -i&lt;/code&gt; if some of the changes in the working copy belong in the next commit.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;jj edit&lt;/code&gt; to hop between commits and work on each until completion. Perhaps &lt;code&gt;jj new -A ...&lt;/code&gt; one or more times when when you don’t want certain changes in a commit: make changes across however many of those commits, then &lt;code&gt;jj squash&lt;/code&gt; them all into your original commit when you’re happy. (You’re basically creating a new git branch!)&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Concurrent commits workflow&lt;br /&gt;
Work on several commits simultaneously, using combinations of &lt;code&gt;jj squash&lt;/code&gt;, &lt;code&gt;jj new&lt;/code&gt;, and &lt;code&gt;jj rebase&lt;/code&gt; to make make reversible changes while having a clean &lt;code&gt;jj log&lt;/code&gt; at the end. In essence, this is point (3.4) above but with room for added complexity.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’m not a VCS expert, and my projects are small, but I’ve gravitated to (1) and (2) without feeling any mental strain of keeping track of where this and that change is and what CLI operations avoid x problem or y merging conflict (which, by the way, jj permits! Jujutsu treats conflicts as first-class, so conflicts and their resolutions percolate through ancestors and descendants without problem.)&lt;sup&gt;&lt;a href=&quot;#fn-3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Perhaps the most exciting part of using jj is fearlessly making changes and experimenting. This has been an extraordinary boon for my learning about how to use jj in accordance to its VCS concepts. The reason is jj’s operation log (&lt;code&gt;jj op log&lt;/code&gt;) and the ability to &lt;strong&gt;revert back to &lt;em&gt;any&lt;/em&gt; past repository state&lt;/strong&gt; (&lt;code&gt;jj op restore OPERATION_ID&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;jj op restore&lt;/code&gt;, I can merge branches, create conflicts, resolve conflicts, mess up… then go back as if it never happened. I can make sweeping repository changes without fear! In git, I’d be walking on egg shells, worried about the potential to ruin the entire state of my local repository if I’m not careful…&lt;/p&gt;
&lt;p&gt;I’d definitely recommend trying jj out if you haven’t already! Easily turn a git repo into a jj repo with a git backend: &lt;code&gt;jj git init \-\-colocate&lt;/code&gt;.&lt;/p&gt;
&lt;footer&gt; &lt;h2&gt;&lt;p&gt;Footnotes&lt;/p&gt;&lt;/h2&gt; &lt;ol&gt; &lt;li&gt;  &lt;a href=&quot;#fnref-1&quot;&gt;[1]&lt;/a&gt; &lt;div&gt; &lt;p&gt;I found &lt;a href=&quot;https://www.youtube.com/watch?v=bx_LGilOuE4&quot;&gt;this talk&lt;/a&gt; by the originator of jujutsu, Martin von Zweigbergk, a senior software engineer at Google, especially insightful with respect to the technical merits of jj over git. If jj is meant to potentially eventually handle Google’s gigantic monorepo (86 terabytes of data, ~2 billion lines of code across 9 million source files!), then surely there’s something to it! The talk justifies the existence of jj.&lt;/p&gt;  &lt;a href=&quot;#fnref-1&quot;&gt;↩&lt;/a&gt; &lt;/div&gt; &lt;/li&gt;&lt;li&gt;  &lt;a href=&quot;#fnref-2&quot;&gt;[2]&lt;/a&gt; &lt;div&gt; &lt;p&gt;Without the &lt;code&gt;-u&lt;/code&gt; flag, &lt;code&gt;jj squash -i&lt;/code&gt; effectively behaves like &lt;code&gt;git commit \-\-amend&lt;/code&gt; when your working copy has a description (e.g., with &lt;code&gt;jj desc -m &quot;...&quot;&lt;/code&gt; or if you &lt;code&gt;jj edit&lt;/code&gt; to a past commit). Without a commit, &lt;code&gt;jj squash -i&lt;/code&gt; is like &lt;code&gt;git commit \-\-amend \-\-no-edit&lt;/code&gt;.&lt;/p&gt;  &lt;a href=&quot;#fnref-2&quot;&gt;↩&lt;/a&gt; &lt;/div&gt; &lt;/li&gt;&lt;li&gt;  &lt;a href=&quot;#fnref-3&quot;&gt;[3]&lt;/a&gt; &lt;div&gt; &lt;p&gt;A great resource on the matter of “conflicts as first-class” is &lt;a href=&quot;https://youtu.be/LV0JzI8IcCY?si=n0xrGZfRx6vZGNmr&amp;amp;t=553&quot;&gt;this talk&lt;/a&gt; by the mind behind jj.&lt;/p&gt;  &lt;a href=&quot;#fnref-3&quot;&gt;↩&lt;/a&gt; &lt;/div&gt; &lt;/li&gt; &lt;/ol&gt; &lt;/footer&gt;</description><pubDate>Thu, 27 Mar 2025 08:35:00 GMT</pubDate><category>VCS</category><category>Tips and Tricks</category></item><item><title>A surprising upside to vc-dir marks over magit</title><link>https://kristofferbalintona.me/posts/202411270440/</link><guid isPermaLink="true">https://kristofferbalintona.me/posts/202411270440/</guid><description>&lt;p&gt;Vc.el is Emacs’s built-in interface for version control CLI tools. With respect to Git, magit has for several years now been the most favored (with respect to popularity) Git&lt;sup&gt;&lt;a href=&quot;#fn-1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; interface. At least in the r/emacs subreddit, magit is frequently considered a killer-feature—an irreplaceable feature unique to Emacs.&lt;/p&gt;
&lt;p&gt;For the most part I am inclined to agree. However, in the last few months, I’ve been regularly experimenting using &lt;code&gt;vc&lt;/code&gt;. This is in part motivated by recent improvements to &lt;code&gt;vc&lt;/code&gt;’s implementation of Git, and another part motivated by a curiosity to see whether I’d end up enjoying some parts of the &lt;code&gt;vc&lt;/code&gt;-way of doing things over the magit-way of doing things.&lt;sup&gt;&lt;a href=&quot;#fn-2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;On such insight has been the surprising benefits of calling &lt;code&gt;vc-dir&lt;/code&gt; instead of &lt;code&gt;magit-status&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The nicety of &lt;code&gt;vc-dir&lt;/code&gt; marks &lt;a href=&quot;#the-nicety-of-vc-dir-marks&quot;&gt;  
§
&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In &lt;code&gt;magit-status&lt;/code&gt;, I would typically stage various files and hunks and commit them when ready. &lt;code&gt;vc&lt;/code&gt; does not use Git’s “staging” concept (because not all version control systems use such a concept). So instead, to achieve something analogous, a user might call &lt;code&gt;vc-dir&lt;/code&gt; (or, much more commonly for me, &lt;code&gt;project-vc-dir&lt;/code&gt;), mark edited files (or entire directories!) using &lt;code&gt;vc-dir-mark&lt;/code&gt; (&lt;code&gt;m&lt;/code&gt;), and check out (commit) those changes via &lt;code&gt;vc-next-action&lt;/code&gt; (&lt;code&gt;v&lt;/code&gt;) or see a diff of those changes with &lt;code&gt;vc-diff&lt;/code&gt; (&lt;code&gt;=&lt;/code&gt;) and commit the changes in that diff-mode buffer (&lt;code&gt;v&lt;/code&gt;).&lt;sup&gt;&lt;a href=&quot;#fn-3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;A problem for the magit interface, however, is &lt;em&gt;visual clarity&lt;/em&gt; when it comes to focusing on changes in a certain subset of files. For example, sometimes I am working with changes in files within a single directory alongside many other uncommitted changes elsewhere in the repository. To make a commit with magit, I would need to navigate past or fold changed hunks/files I want to ignore. With many unstaged changes this means many files to ignore. &lt;strong&gt;This leads to a significant reduction in visual clarity since I end up having to ignore more files than I’m currently working on.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There is a subtle solution in vc.el. The secret lies in the fact that &lt;em&gt;marks, which can be used to selectively create diff buffers and commit changes from those buffers, are preserved&lt;/em&gt; in &lt;code&gt;vc-dir&lt;/code&gt; buffers after changes are committed. A mark is only removed once the corresponding line is removed via something like &lt;code&gt;vc-dir-hide-up-to-date&lt;/code&gt; (&lt;code&gt;x&lt;/code&gt;) or &lt;code&gt;vc-dir-kill-line&lt;/code&gt; (&lt;code&gt;C-k&lt;/code&gt;). Marks are magical because they let me &lt;em&gt;focus&lt;/em&gt; on the files relevant to my current work: I can mark the files and directories I am focusing on &lt;strong&gt;then call &lt;code&gt;vc-next-action&lt;/code&gt; in the &lt;code&gt;vc-dir&lt;/code&gt; buffer to commit changes only in those files and directories&lt;/strong&gt; (or, if I want to be selective about the hunks I commit: first call &lt;code&gt;vc-diff&lt;/code&gt; to see the diff of just the marked files, kill/remove the hunks I don’t want to commit, then &lt;code&gt;vc-next-action&lt;/code&gt;).Then I can write more changes and make more commits in the same way. (Furthermore, while this &lt;code&gt;vc&lt;/code&gt; workflow might seem like it would necessarily involve many more keystrokes, I found this not to be the case under certain scenarios.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In short,&lt;/strong&gt; with vc.el, a really efficient workflow is&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;vc-diff&lt;/code&gt; or &lt;code&gt;vc-root-diff&lt;/code&gt; → &lt;code&gt;vc-next-action&lt;/code&gt; and&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vc-dir&lt;/code&gt; (with persistent marks) → &lt;code&gt;vc-diff&lt;/code&gt; → &lt;code&gt;vc-next-action&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The latter is unique to vc.el, and a feature I don’t think the magit UI can replicate. So while magit can sometimes become visually cluttered, &lt;code&gt;vc-dir&lt;/code&gt; marks let users focus on their current work by easily excluding &lt;em&gt;from view&lt;/em&gt; files and directories one is not concerned with currently.&lt;/p&gt;
&lt;h2&gt;A caveat &lt;a href=&quot;#a-caveat&quot;&gt;  
§
&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Historically, I’ve found this especially fruitful when working on my literate Emacs config&lt;sup&gt;&lt;a href=&quot;#fn-4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt; (wherein any “single change” involves changes to the .org file as well as the resulting tangled file(s)) and Emacs packages. Still, I think I prefer magit in cases where the staging functionality of Git is crucial. For instance, I have a repository that houses all my org notes and org-agenda files. There are frequently many changes for this repository, so the convenience of staging/unstaging has been crucial for me. In cases like these, I haven’t found an equivalent for vc.el yet.&lt;/p&gt;
&lt;h2&gt;Changelog &lt;a href=&quot;#changelog&quot;&gt;  
§
&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;time&gt; &lt;span&gt;Mar 25, 2025&lt;/span&gt;  &lt;/time&gt;
&lt;ul&gt;
&lt;li&gt;Add clarifying prose to paragraph.&lt;/li&gt;
&lt;li&gt;Change formatting of “vc.el” and “magit.”&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;footer&gt; &lt;h2&gt;&lt;p&gt;Footnotes&lt;/p&gt;&lt;/h2&gt; &lt;ol&gt; &lt;li&gt;  &lt;a href=&quot;#fnref-1&quot;&gt;[1]&lt;/a&gt; &lt;div&gt; &lt;p&gt;magit is an interface for Git, whereas vc.el is an interface for other version control tools, albeit older ones, some of which may follow a non-decentralized version control paradigm (read the Emacs Info node &lt;code&gt;(emacs) Version Control Systems&lt;/code&gt; for more information):&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;CVS&lt;/li&gt;
&lt;li&gt;Subversion (svn)&lt;/li&gt;
&lt;li&gt;SCCS&lt;/li&gt;
&lt;li&gt;CSSC&lt;/li&gt;
&lt;li&gt;Mercurial (hg)&lt;/li&gt;
&lt;li&gt;Bazaar (bzr)&lt;/li&gt;
&lt;li&gt;SRC (src)&lt;/li&gt;
&lt;/ul&gt;  &lt;a href=&quot;#fnref-1&quot;&gt;↩&lt;/a&gt; &lt;/div&gt; &lt;/li&gt;&lt;li&gt;  &lt;a href=&quot;#fnref-2&quot;&gt;[2]&lt;/a&gt; &lt;div&gt; &lt;p&gt;A philosophical tangent: the interface of a tool (digital or analog/physical) influences how users interact with that tool and how users accomplish the purposes of the tool. As such, what seems at first as a subtle difference in workflow or interface to achieve the same end almost always overlooks aspects relevant to the workflow’s effectiveness.&lt;/p&gt;&lt;p&gt;For instance, users of Emacs will readily recognize that Emacs is thoroughly characterized by an “emacs-y” way of doing things. The total network of keybinds, conventions, and procedures of using Emacs might accomplish the same purposes as other software (combined or standalone), but this specific network of “ways to interface with Emacs-as-a-tool” enables Emacs to offer a uniquely effective experience.&lt;/p&gt;  &lt;a href=&quot;#fnref-2&quot;&gt;↩&lt;/a&gt; &lt;/div&gt; &lt;/li&gt;&lt;li&gt;  &lt;a href=&quot;#fnref-3&quot;&gt;[3]&lt;/a&gt; &lt;div&gt; &lt;p&gt;If I want to commit changes with much fewer unstaged changes present, then a faster method would be: call &lt;code&gt;vc-root-diff&lt;/code&gt; (&lt;code&gt;C-x v D&lt;/code&gt;), modify the diff-mode buffer accordingly with &lt;code&gt;diff-hunk-kill&lt;/code&gt; (&lt;code&gt;k&lt;/code&gt;) or &lt;code&gt;diff-delete-other-hunks&lt;/code&gt; (&lt;code&gt;C-c RET n&lt;/code&gt;; a recent addition to Emacs-30), then commit the changes with &lt;code&gt;vc-next-action&lt;/code&gt; (&lt;code&gt;v&lt;/code&gt;). &lt;a&gt;&lt;/a&gt;&lt;/p&gt;  &lt;a href=&quot;#fnref-3&quot;&gt;↩&lt;/a&gt; &lt;/div&gt; &lt;/li&gt;&lt;li&gt;  &lt;a href=&quot;#fnref-4&quot;&gt;[4]&lt;/a&gt; &lt;div&gt; &lt;p&gt;See also &lt;a href=&quot;#target-1&quot;&gt;footnote 3&lt;/a&gt;.&lt;/p&gt;  &lt;a href=&quot;#fnref-4&quot;&gt;↩&lt;/a&gt; &lt;/div&gt; &lt;/li&gt; &lt;/ol&gt; &lt;/footer&gt;</description><pubDate>Wed, 27 Nov 2024 04:40:00 GMT</pubDate><category>Emacs</category><category>VCS</category><category>Tips and Tricks</category></item></channel></rss>