Julia Evans

Julia Evans' personal website is a treasure trove of insightful and engaging content primarily focused on technology, software engineering, and learning. Evans, a renowned software engineer, uses her platform to share her extensive knowledge through detailed blog posts, captivating illustrations, and personal anecdotes. Her writing style is approachable and humorous, making even complex technical topics accessible to a wide audience. The website features articles on various subjects, including Linux internals, programming languages, and debugging techniques. Evans' passion for technology and her ability to explain intricate concepts clearly shine through her work, inspiring and educating readers. Whether you're a seasoned developer or just starting your coding journey, Julia Evans' website offers valuable insights and a refreshing perspective on the world of software engineering.

Thread Of Notes

Moving away from Tailwind, and learning to structure my CSS

The author reflects on their journey with CSS, initially embracing Tailwind for its structure, but now transitioning to vanilla CSS. This change was prompted by a desire to understand CSS more deeply and improve their skills. The author details the new CSS structure, including an imported reset, component-based styling, predefined color variables, and a font size system adapted from Tailwind. They also utilize utility classes, establish a base for global styles, and manage spacing consciously. Responsive design relies more on CSS grid layouts, departing from Tailwind's media query-focused approach. The build system employs esbuild for production bundling, although not essential during development. The author outlines key reasons for the migration, citing the reliance on a build system, improved CSS knowledge, the limitations of Tailwind, and a desire for more semantic HTML. They conclude by discussing features like @layer, and acknowledging the benefits of Tailwind while still finding value in its principles.

Links to CSS colour palettes

The author stopped using Tailwind but missed its convenient color palettes. They sought alternatives to Tailwind's color system to improve their workflow. They desired pre-defined color palettes, particularly for ease of use in CSS. The author asked for palette suggestions on Mastodon and decided to compile the responses. The post lists several color palettes like uchū, flexoki, and reasonable colours. Various other palettes and design systems are also provided as options for consideration. A range of color scheme generators are featured, although the author finds them difficult to use. Additional color tools, including colorblindness information and the oklch CSS function, are presented. The author hopes the resource will be useful for themselves and others seeking CSS color solutions.

Testing Vue components in the browser

The author aims to write frontend JavaScript without relying on Node.js. A significant challenge encountered is the lack of effective testing methods for frontend code. Previous attempts with Playwright were deemed slow and required Node.js. To address this, the author explored running tests directly within the browser tab, inspired by Alex Chan's work on a minimalist in-browser unit testing framework. While Chan's approach focused on unit tests, the author sought to perform end-to-end integration tests for Vue components without a Node.js build process. The author used QUnit as the testing framework, appreciating its "rerun test" button for debugging. The process involved setting up Vue components by exposing them globally and creating a `mountComponent` function to render them in an invisible, off-page div. Fixture data was managed by resetting the database via a POST request to a dedicated endpoint. A basic test then rendered a component and asserted its content. Key issues encountered included waiting for asynchronous operations to complete, leading to the development of a `waitFor()` function. Identifying the correct element or condition to wait for proved challenging, prompting suggestions for refactoring the application for better reliability. The author also experimented with adding CSS classes for element identification, noting that libraries like Testing Library recommend more accessible approaches. Form handling presented difficulties as simply setting values wasn't sufficient; relevant DOM events needed to be dispatched. This highlighted why UI testing libraries might simplify such tasks. Test coverage was partially assessed using Chrome's built-in developer tools, although the process required specific configurations and actions to work with bundled JavaScript. Despite the learning curve, the author found the experience enjoyable and expressed optimism about building a confident test suite for their frontend projects. They are also considering further exploration of libraries like Testing Library, which offers a UMD build compatible with browser-only execution, and pondering how to integrate browser-primarily tests into CI environments.

Examples for the tcpdump and dig man pages

The author was inspired by the usefulness of examples in man pages and improved them for the dig and tcpdump tools. The primary goal was to provide the most basic examples for infrequent users or beginners. This approach of targeting beginners and infrequent users has proven effective and well-received. The author found the review process valuable, leading to more accurate information discoverable within man pages. They learned about useful tcpdump features, such as the `-v` flag when saving packets. Despite a general skepticism towards documentation, the author is now optimistic about the potential for high-quality, accurate man pages. To avoid learning roff, a custom markdown-to-roff script was created for the tcpdump man page updates. This involved parsing Markdown's Abstract Syntax Tree and emitting roff code. The author also explored the history and evolution of roff and the mandoc project. Curiosity was sparked regarding the technical and cultural differences in documentation practices between BSD and Linux systems.

Notes on clarifying man pages

The author reflects on improving man pages, prompted by difficulties finding information in them. They consider incorporating cheat sheets, like those found in `perlcheat`, for quick reference. Inspired by examples from the `rsync` and `strace` man pages, they suggest options summaries and category-based organization. The author emphasizes the popularity of examples, referencing OpenBSD's approach and the `curl` man page's example-per-option strategy. They also highlight the value of table formats, as seen in `man ascii`, for easier information scanning. The author contrasts the GNU project's use of "info" manuals over man pages, noting the limitations of terminal-based man pages. They discuss tools like `tldr.sh` and Dash, and contemplate how to enhance man pages within their constrained format. They particularly value examples, but are open to other design improvements.

Some notes on starting to use Django

The author enjoys learning mature technologies, and recently started using Django for a website project. Django's explicit structure, with key files like `urls.py` and `models.py`, helps the author easily understand the project. Django's built-in admin interface provides easy data management, and the ORM simplifies database interactions, with automatic migrations streamlining database changes. The author appreciates the Django documentation and prefers using SQLite for smaller projects due to its simplicity. Django's "batteries-included" approach simplifies tasks like CSRF protection and email configuration. The author is still learning, but finds Django's feature set appealing. The settings file remains somewhat daunting due to its global nature and lack of immediate typo detection. The author experiments with setting up email using different backends. The author is excited to explore Django, having primarily worked with single Go binaries or static sites. The author credits Marco Rogers for encouraging the use of ORMs.

A data model for Git (and other docs updates)

The author decided to improve Git's documentation after previously writing blog posts and zines. They collaborated with Marie to create a "data model" document explaining Git's core concepts like objects, references, and the index. The author updated introductions to several Git man pages like `git push` and `git pull`. They realized that a more evidence-based approach was needed to identify documentation problems. The author asked for test readers on Mastodon to provide feedback on the existing documentation. Test readers identified confusing terminology, unclear sentences, and inconsistencies in the documentation. This feedback helped the author improve the man pages for `git add`, `git checkout`, `git push`, and `git pull`. The author found the changes for `git push` and `git pull` most interesting, including definitions for "upstream branch" and "push refspec". Contributing to Git involved learning its development process, including using the Discord server and GitGitGadget. They found the Git community welcoming and received help from many contributors. The author also created a custom Git list viewer to navigate mailing list archives.

Notes on switching to Helix from vim

The author switched from Vim/Neovim to Helix, a text editor, drawn by the ease of language server integration. After years of struggling with complex Vim configurations, Helix's built-in features were appealing. Helix's search and quick reference features are particularly helpful and appreciated. The author readily adapted to Helix's keybindings, although some Vim habits required adjustment. The author found Helix's multiple cursors and buffer switching preferable to Vim's macros and tabs. However, the author notes some annoyances, including reflow problems with lists and lack of persistent undo and autoreloading. Despite these frustrations, the author has adapted to Helix, finding the transition easier than expected. The editor's simple configuration is a major advantage over the complexities of their previous setup. The author primarily uses Helix within a terminal, organizing projects with dedicated windows. The author is satisfied with Helix after three months of use but remains open to possibly returning to Vim.

New zine: The Secret Rules of the Terminal

The author has released a new zine called "The Secret Rules of the Terminal" that explains how the terminal works and provides tips and tricks for using terminal programs. The author has been using the terminal daily for 20 years but still had many misunderstandings about how it works. The terminal has many tiny inconsistencies, such as sometimes allowing arrow keys to move around and sometimes not. The "rules" for how the terminal works are hard to understand because it's made up of many different pieces of software. The zine explains how the four pieces of the terminal (shell, terminal emulator, programs, and TTY driver) fit together and provides core conventions for how things work in the terminal. The author learned a surprising amount while writing the zine, including how to configure their shell and understand escape codes. The zine is available for purchase as a PDF or print version, and a 15-pack of all the author's zines is also available. The author received help from many people, including a technical reviewer who wrote PuTTY and someone who understands the terminal's inner workings. The print version will ship in August, and the author needs to wait for orders to come in to determine how many to print. The zine is available for $12, and the author hopes it will help readers understand and use the terminal better.

Using `make` to compile C programs (for non-C-programmers)

The author is not a C programmer but occasionally needs to compile C/C++ programs from source. They used to rely on others to compile the programs, but since switching to a Mac, they've had to learn how to compile programs themselves. To compile a C program, one needs to install a C compiler, install the program's dependencies, run ./configure if needed, and run make. The author explains how to install a C compiler, install dependencies, and run ./configure. They also discuss common issues that can arise during the compilation process, such as dependency problems and compiler errors. The author explains how to fix these issues by passing flags to the compiler and linker. They also provide tips on how to build specific files, look at how other packaging systems built the same C program, and install the binary. The author concludes that understanding the basics of how C programs work can be useful, even if one is not a C programmer.

Standards for ANSI escape codes

ANSI escape codes are used to improve terminal usability, but they are not fully standardized, leading to reliability issues. The author learned about ANSI escape codes while studying the terminal and wanted to understand the standards surrounding them. Escape codes are used by terminal emulators to communicate with programs, and they come in two types: input codes for keypresses and mouse movements, and output codes for tasks like coloring text and moving the cursor. The ECMA-48 standard, published in 1976, defines general formats for escape codes and some specific codes, but it's not exhaustive. Xterm control sequences are another set of escape codes that have been widely implemented by terminal emulators, although they are not a formal standard. The terminfo database, managed by ncurses, stores escape codes for various terminals, and some programs use it to determine which codes to use. However, some programs choose not to use terminfo and instead hardcode a "single common set" of escape codes that work in most terminal emulators. There is no clear agreement on what this common set is, but it likely includes codes from the VT100, ECMA-48, and xterm. Despite challenges, standardizing ANSI escape codes could lead to a richer terminal experience, and the author hopes that a clearer standards landscape could facilitate innovation in terminal emulators and applications.

How to add a directory to your PATH

To add a directory to your PATH, you first need to determine which shell you are using, which can be done by running the command ps -p $$ -o pid,comm=. The default shell on Linux is bash, while on Mac OS it is zsh. Next, you need to find your shell's config file, which is usually ~/.zshrc for zsh, ~/.bashrc for bash, and ~/.config/fish/config.fish for fish. However, bash's config file can be one of three options: ~/.bashrc, ~/.bash_profile, or ~/.profile, and you may need to test which one is being used. After finding your shell's config file, you need to figure out which directory to add to your PATH. This can be done by checking the installation instructions for the program you are trying to run, or by using a command specific to the installer, such as npm config get prefix for Node/npm. Once you have found the correct directory, you should double-check that it is correct by trying to run the program from that directory. Then, you need to edit your shell config file to add the directory to your PATH. The syntax for this varies depending on the shell, with bash and zsh using export PATH=$PATH:~/directory, and fish using set PATH $PATH ~/directory. Finally, you need to restart your shell for the changes to take effect. If the program still doesn't work, you may need to add the directory to the beginning of your PATH instead of the end, or you may need to add the directory to your PATH in a different way if you are running the program from an IDE or cron job. Additionally, some installers may provide a script to automatically set up your PATH, which can be run by adding a line to your shell config file. However, you may prefer to manually add the directory to your PATH instead.

Some terminal frustrations

A survey was conducted to gather information on the most frustrating things about using the terminal, with 1600 people responding. The respondents were mostly experienced users, with 40% having used the terminal for 21+ years and 95% for at least 4 years. The top categories of frustrations included remembering syntax, switching terminals, color issues, keyboard shortcuts, and copy and paste problems. Other common frustrations included discoverability, steep learning curve, history, bad documentation, and scrollback issues. Some users also mentioned feeling uneasy or scared when using the terminal, and others had issues with shell scripting, inconsistent command line arguments, and performance. The survey also revealed that many users have trouble keeping dotfiles in sync across different systems and managing windows with tmux tabs and terminal tabs. Some users mentioned feeling overwhelmed by the terminal's capabilities and wanting a more streamlined experience. The survey results will be used to inform the creation of a zine about the terminal. Overall, the survey highlights the complexities and challenges of using the terminal, even for experienced users.

What's involved in getting a "modern" terminal setup?

The author reflects on what constitutes a "modern" terminal experience, which includes features like multiline support for copy and paste, infinite shell history, and 24-bit color support. They acknowledge that achieving this experience can be difficult due to the many components involved, including the shell, terminal emulator, and text editor. The author's personal approach to achieving a modern terminal experience involves using the fish shell, a terminal emulator with 24-bit color support, and neovim with a custom configuration. For those who don't want to spend a lot of time on configuration, the author suggests using fish or zsh with oh-my-zsh, a terminal emulator with 24-bit color support, and a text editor like micro or helix. However, the author notes that even with these options, getting a modern terminal experience can be challenging due to issues with the shell, text editor, and individual applications. The shell can be particularly problematic, as popular shells like bash and zsh require customization to provide a satisfactory experience. The text editor can also be an issue, with options like vim and emacs requiring significant configuration, while nano provides a limited experience. The author notes that individual applications can also cause problems, and that debugging these issues can be time-consuming. Ultimately, the author concludes that achieving a modern terminal experience requires patience, experimentation, and a willingness to make small changes and adapt to new tools and configurations.

"Rules" that terminal programs follow

Programs in the terminal behave consistently despite no standards. They follow certain rules, such as noninteractive programs quitting when pressing Ctrl-C, TUIs quitting when pressing 'q', and REPLs quitting when pressing Ctrl-D on an empty line. Most programs support readline keybindings, disable colors when writing to a pipe, and use '-' to mean stdin/stdout. These rules are descriptive, not prescriptive, and understanding them helps in using terminal programs effectively.

Why pipes sometimes get "stuck": buffering

Buffering is common in terminal programs to improve performance by grouping output until a certain size threshold is met. This can cause issues when data is added slowly to a pipe, as programs may buffer their output and never write it. Grep and similar programs default to block buffering when writing to pipes but use line buffering when writing to terminals, explaining why the command "tail -f /some/log/file | grep thing1 | grep thing2" may not display output. Several commands buffer their output, including grep, sed, awk, tcpdump, and jq, while commands like tail, cat, and tee do not. Programming languages like C, Python, Ruby, and Perl may also buffer output, with various methods to disable buffering. When Ctrl-C is pressed on a pipe, the program's output buffer may be lost, as the signal is received before the buffer can be flushed. Buffering also occurs when redirecting to a file, but it generally behaves as expected, with the contents of the buffer being written before the program exits. To avoid buffering, one can run a program that finishes quickly, use the "--line-buffered" flag with grep, rewrite the command using awk, use stdbuf, or use unbuffer to force the program to behave as if writing to a terminal. The ideal solution depends on the specific situation, with unbuffer being a reliable choice for its consistent behavior. While buffering is generally not a common issue, it can arise when data is slowly added to a pipe. An environment variable to disable buffering could be useful, but its design and implementation present challenges.

Importing a frontend Javascript library without a build system

The author prefers writing JavaScript without a build system and shares their experience with importing libraries without using a build system. They explain the three main types of JavaScript files a library might provide: "classic" global variable files, ES modules, and CommonJS modules. The author demonstrates how to find the files in a library's NPM build and discusses using importmaps to use ES modules in the browser. They also mention using esm.sh to convert CommonJS modules into ES modules. The author provides examples of using Chart.js, @atcute/oauth-browser-client, and @atproto/oauth-client-browser, discussing the different approaches for each. They note that using importmaps requires browser support, which might be a concern for older browsers. The author also mentions using esbuild as an alternative to importmaps. Finally, they summarize the three types of JavaScript files and how to identify and use them.

New microblog with TILs

The author created a new section on their site called "TIL" (Today I Learned) to save interesting tools and facts they post on social media. The goal is to have a place to store these tidbits without having to write a full blog post. The author often posts "cool things" on Mastodon and Bluesky but didn't have a place to keep track of them. This new section is inspired by Simon Willison's TIL blog, but the author's posts are much shorter. The author made a new folder for the TIL section, added custom styling, and set up a separate RSS feed. The TIL section is mostly for the author's own use, as a way to keep track of useful links and tools. The author has been using the section for a couple of weeks and has found it to be working well. The author is a fan of the "POSSE" (post on your own site, syndicate elsewhere) idea but finds it easier to identify specific categories of content they want to keep on their own site. The author has email lists and RSS feeds for their blog posts and comics, and may add a summary of TIL posts to their mailing list. The author prefers to keep some content ephemeral, such as polls and jokes, and only archives specific categories of content.

ASCII control characters in my terminal

The author explores the concept of control codes in the terminal, such as Ctrl-A, Ctrl-C, and Ctrl-W, and how they work. There are 33 ASCII control characters, which can be categorized into codes handled by the operating system's terminal driver, codes that correspond to literal key presses, and codes used by readline. The author notes that there is no real structure to which codes are in which categories, as they evolved organically. There are only 33 control codes, which means that if you want to use Ctrl-1 as a keyboard shortcut, it's not meaningful, as it's the same as pressing 1. The author also notes that Ctrl+Shift+C is not a control code and its behavior depends on the terminal emulator. The official ASCII names for control codes are not very useful, as they were originally defined for telegraph machines and have since been repurposed. The author finds it hard to use Ctrl-M and Ctrl-I as keyboard shortcuts, as they are equivalent to Enter and Tab, respectively. The author provides a Python script to identify what control codes get sent when pressing various key combinations. The author notes that some control codes, such as Ctrl-W and Ctrl-U, can be handled differently depending on whether the terminal is in canonical or noncanonical mode. Finally, the author acknowledges that there are many caveats and conflicts when it comes to control codes, and that not all of this information is necessarily useful in practice.

Using less memory to look up IP addresses in Mess With DNS

The author of Mess With DNS was experiencing issues with the program running out of memory and getting OOM killed, which led to problems with the backup script. To solve this, the author decided to try to make Mess With DNS use less memory. The program loads a database of IP addresses into memory, which was using around 117MB of memory. The author tried three different approaches to reduce memory usage. The first approach was to use SQLite to store the data on disk, which solved the initial memory goal but had issues with storing IPv6 addresses and was 500 times slower than the original binary search. The second approach was to use a trie, but it used more memory and was slower than the original binary search. The third approach was to make the array use less memory by deduplicating the Name and Country fields, which brought memory usage from 117MB to 65MB, and then switching to netip.Addr instead of net.IP, which saved another 20MB of memory, bringing the total to 46MB. The author was able to save 70MB of memory in total.

Some notes on upgrading Hugo

The author of the blog post decided to upgrade their Hugo version from 0.40 to 0.135, despite the old version working fine. They documented the changes they had to make during the upgrade process, which included replacing template calls, updating page references, and flipping the meaning of "next" and "previous" in their theme. The author also had to update their Markdown files to work with the new Goldmark renderer, which replaced the old Blackfriday renderer. This involved making changes to how they wrote Markdown, including mixing HTML and Markdown, using angle quotes, and indenting nested lists. The author also had to configure the Goldmark renderer to turn off code highlighting and use the old method for generating heading IDs. They wrote a script to compare the output of the Blackfriday and Goldmark renderers and used it to identify and fix issues with their Markdown files. The author notes that the upgrade process was time-consuming and not without its challenges, but ultimately thinks it was worth it to have a more future-proof Markdown renderer. They also mention that they will likely have to make similar changes when upgrading their other site, wizardzines.com, which is still using an older version of Hugo.

Terminal colours are tricky

Yesterday, I was thinking about how long it took me to find a terminal colorscheme I was mostly happy with. I asked people on Mastodon about their issues with terminal colors and got many interesting responses. One common complaint was "blue on black" being hard to read. I use a modified Solarized theme for my terminal, which I'm very happy with. Some newer tools like fd and bat have theme support, which can help with contrast issues. I also mentioned the "minimum contrast" feature in some terminals, which automatically adjusts colors for better contrast. Other issues include TERM being set to the wrong thing, picking "good" colors, making nethack/mc look right, commands disabling colors when writing to a pipe, unwanted color in ls and other commands, and the colors in vim.

Some Go web dev notes

The author shares their experiences working on a Go website, discussing various tools and techniques they've learned along the way. They mention using sqlc to generate Go code from SQL queries, which simplifies the process of interacting with databases. The author also appreciates Go's built-in web server and toolchain, as well as its systems-oriented nature, which makes it easy to perform low-level operations like running an ioctl. They compare their experience with Go to their experience with Rails, noting that while Rails feels magical, it's harder to return to after a break. The author also highlights new features in Go, such as setting a GC memory limit and improved routing support in the standard library.

Reasons I still love the fish shell

Fish is a popular shell offering an enhanced user experience with several key features. These include automatic history suggestions, smart autocompletion, easy pasting of multiline commands, a user-friendly tab completion interface, and a well-designed default prompt with git integration. Fish also boasts a comprehensive history system with a search function and avoids terminal breakage caused by accidentally sending binary data. It disables the potentially disruptive Ctrl+S key combination, provides a global path modification utility, and features syntax highlighting for non-existent commands. Additionally, Fish offers simplified loop syntax, easier multiline editing, and the convenient Ctrl+left/right arrow key shortcuts for word navigation. While there may be limited instructions for integrating certain tools with Fish, it has become more widely accepted, and users can fall back on POSIX shells when necessary. Fish is particularly suitable for individuals who prioritize a minimal configuration, appreciate its default settings, and desire a shell with an intuitive and feature-rich design.

Migrating Mess With DNS to use PowerDNS

Mess With DNS is a platform for learning DNS functionality by creating and editing records. The original DNS implementation had limitations, including disallowed domain names with underscores, lack of CNAME record support, and absence of SVCB and HTTPS record types. To address these issues, the author integrated PowerDNS, an open source DNS server with an HTTP API, replacing the previous DNS implementation. This presented challenges in intercepting DNS queries and designing an API that met the frontend's needs. For error handling, the author tailored user-facing error messages to provide clearer information, handling PowerDNS API error responses and performing basic input validation. SQLite replaced Postgres for database management due to OOM kills experienced with Postgres. The Vue.js library was upgraded to version 3, accompanied by a transition to using built-in browser form validation tools and implementation of a global state management store for frontend management. The project was divided into phases for manageable implementation, including Vue upgrade, state management store creation, backend API redesign, and PowerDNS integration. The updated website has been released and functions well, resolving previous DNS issues reported by users.

Go structs are copied on assignment (and other things about Go I'd missed)

The author, a casual Go programmer, encountered a bug that revealed a fundamental misunderstanding of how structs are copied during assignment in Go. This misunderstanding stemmed from the fact that structs are copied on assignment, not referenced, which led to unexpected behavior when modifying a struct after assigning it to another variable. The author was surprised by this behavior, as their experience with other languages led them to believe that variables were typically passed by reference. This misconception was further complicated by the author's understanding of pass-by-value and pass-by-reference in function arguments, which they had not extended to variable assignments. The author also discovered that sub-slices in Go share the same backing array as the original slice, meaning appending to a sub-slice can unintentionally modify the original. Additionally, the author gained clarity on the difference between value and pointer receivers in Go methods, understanding that pointer receivers are necessary when a method needs to mutate the struct it's called on. The author praised the "100 Go Mistakes" resource for its clear and concise format, which allowed them to quickly identify and learn from common Go pitfalls. Finally, the author listed other valuable Go resources, including "Go by Example," "go.dev/play," and static analysis tools like "staticcheck" and "golangci-lint."

Entering text in the terminal is complicated

Entering text in the terminal can be challenging due to inconsistencies between programs. Basic features like arrow keys may not be supported. Readline, a library providing text editing capabilities, is widely used and supports various keyboard shortcuts. Programs without readline support can be enhanced using rlwrap. Some programs use custom input systems that mimic readline or provide additional features. Understanding the input system being used can enhance predictability. Common shortcuts include Ctrl+A for the beginning of the line, Ctrl+E for the end, and Ctrl+R for history search. Many shells support vi keybindings as an alternative input mode. Diagnosing the input system (no input system, readline, or custom) allows for tailored use of available features. However, there are additional complexities related to text input in the terminal that are not covered in this summary.

Reasons to use your shell's job control

Job control is a feature in your shell that allows you to manage processes by moving them between three states: foreground, background, and stopped. It includes commands like fg, bg, Ctrl+z, jobs, kill, disown, and wait. Fish, bash, and zsh all support job control, but it can be used differently in each shell. Some people prefer job control over using terminal tabs because they like to see all their terminals on the screen at the same time. It can be useful for killing processes that don't respond to Ctrl+C, running GUI apps or CPU-hungry programs in the background, and for setting up environment variables for multiple commands. Job control can also be necessary in situations where you're in single-user mode or SSH'd into a machine without tmux or screen.

New zine: How Git Works!

"How Git Works" is a new zine by Julia Evans that aims to demystify Git, a popular version control system. The zine targets users with years of Git experience who still struggle with it, aiming to clarify common misconceptions and provide a deeper understanding of its internal logic. Evans acknowledges the confusing nature of Git's terminology, error messages, and inconsistent behavior, but emphasizes that handling these quirks becomes routine with experience. The zine focuses on user interface behavior and how it relates to internal Git processes, avoiding excessive emphasis on internals. It also includes a printable cheat sheet and an HTML transcript for accessibility. Despite highlighting Git's complexities, Evans remains enthusiastic about the tool, appreciating its speed, backwards compatibility, and the availability of free hosting options. The zine's creation involved collaboration with various individuals, including Marie Claire LeBlanc Flanagan for clear explanations, Vladimir Kašiković for the cover design, and 66 beta readers for feedback. The zine is available for purchase in PDF or print versions, with print orders shipping in July.

Notes on git's error messages

Git error messages can be confusing, particularly for beginners. The author analyzes several common error messages, highlighting their complexities and offering practical solutions. One challenge with improving these messages is the difficulty in determining whether changes are actually beneficial. The author notes that Git maintenance is complex, involving factors such as international translation and limited funding for improvement efforts. Nonetheless, some suggestions for enhancing error messages include providing clearer distinctions between diverged and behind branches and reducing the overwhelming number of options presented to users when reconciling divergent branches. Additionally, the author emphasizes the importance of understanding Git's internal logic, such as the difference between branches and paths in the context of commands like `git checkout`. By understanding these nuances, users can better interpret error messages and effectively navigate Git's command-line interface.

Making crochet cacti

The author has been experimenting with non-computer hobbies, including crocheting tiny cacti. They share their experiences, including the patterns, yarn types, and techniques they've used. Initially using free patterns, they now prefer purchasing patterns to support creators. They emphasize the joy of modifying patterns and embracing mistakes as learning opportunities. Instead of safety eyes, they embroider eyes and use scrap yarn as stitch markers to simplify the process. The author struggles with counting in crochet but finds ways to overcome this through visual inspection and approximation. They've explored various yarn weights and brands, experimenting with merino, cotton, and acrylic. Hook size seems less consequential for cacti projects. The author has learned basic stitches and approaches new ones by starting patterns and seeking guidance from YouTube videos. They've made items such as an elephant, cacti, and an in-progress mouse. The author appreciates the tactile nature of crocheting, the social aspect, and the minimal space required for materials. They emphasize the forgiveness of crochet, allowing for experimentation and the embrace of mistakes.

Some Git poll results

The author conducted polls on Mastodon to gather insights into how people use and perceive Git, revealing surprising findings. The most common merge strategy is "merge," with only 30% using "rebase" frequently. Despite occasional conflicts, only 14% have lost work due to Git issues in recent years. Many users are unfamiliar with terms like "HEAD," "ref," and "fast-forward," prompting the author to use caution in their usage. Interestingly, most prefer Git over SVN, and there's a split preference between Git and Mercurial. Notably, 71% of users display their current branch in their shell prompt. The author shares results from polls on git concepts like commits, branches, and git environment, providing valuable insights into user perceptions and preferences.

The "current branch" in git

The concept of "current branch" in Git, while seemingly straightforward, presents some ambiguity when examined closely. While the Git glossary defines it as the content of the .git/HEAD file, other interpretations exist, including the output of "git status", the last checked-out branch, and the shell prompt. These interpretations often align but diverge in specific scenarios like detached HEAD states, checked-out tags, and mid-rebase situations. For instance, checking out a tag leads to .git/HEAD storing the commit ID while "git status" displays the tag name for user convenience. Similarly, during a rebase, "git status" highlights the rebase state while the shell prompt might indicate the original branch. Even "git init" introduces a nuance where the "current branch" is automatically set without an explicit checkout. Further complexities arise with bare repositories where "git status" and "git checkout" are inoperable. These inconsistencies highlight the limitations of rigidly defining "current branch" and emphasize the importance of contextual understanding. The definition of "current branch" as the target for new commits, while generally true, falters during a rebase where the commit ultimately lands on the original branch. The idea of "current branch" representing the context for Git operations holds merit, but discrepancies exist, such as "git status" behaving differently in bare repositories. Ultimately, understanding the nuances of "current branch" requires acknowledging its context-dependent nature and relying on a combination of indicators like .git/HEAD, "git status" output, and the last checkout action for a comprehensive understanding.

How HEAD works in git

The author of the article ran a poll on Mastodon asking people how confident they were in their understanding of how HEAD works in Git, and the results showed that many people were uncertain or had no idea. The author initially thought HEAD was a straightforward topic but discovered that it was more complicated than they appreciated after having follow-up conversations with others. HEAD can refer to different things, including the file .git/HEAD, the "revision parameter" HEAD in commands like git show HEAD, and the various ways HEAD is used in Git's output. The file .git/HEAD contains either the name of a branch or a commit ID and determines the current branch in Git. If .git/HEAD contains a commit ID, Git calls that "detached HEAD state," which means there is no current branch. In detached HEAD state, new commits will not be attached to any branch and can be difficult to find or may be deleted by Git's garbage collection. The author explains how to interpret the output of various Git commands that use HEAD, including git status, git log, and merge conflicts. They also suggest that Git's terminology around HEAD could be more consistent and intuitive.

Popular git config options

- Pull Handling: `pull.ff only` or `pull.rebase true` to avoid creating merge commits when upstream branch diverges. - Merge Conflict Readability: `merge.conflictstyle zdiff3` enhances merge conflict visibility by displaying the original code in the middle. - Automated Commit Modification: `rebase.autosquash true` combines `fixup!` commits with their targets during rebasing. - Automatic Stashing: `rebase.autostash true` runs `git stash` before and after rebasing. - Push Automation: `push.default current` or `push.default simple` pushes the current branch to a matching remote branch; `push.autoSetupRemote true` sets up tracking for the first push. - Default Branch: `init.defaultBranch main` creates a `main` branch instead of `master` in new repositories. - Commit Message Enhancement: `commit.verbose true` displays the commit diff in the commit message editor. - Conflict Resolution Automation: `rerere.enabled true` remembers and automates merge conflict resolutions. - Autocorrect: `help.autocorrect 10` allows Git to run autocorrections after a specified delay. - Diff Visualization: `core.pager delta` uses a syntax-highlighting diff viewer; `diff.algorithm histogram` improves function reordering visibility. - Global Gitignore: `core.excludesfile` specifies a global gitignore file. - Separate Git Configs: `includeIf` allows different configurations for personal and work repositories. - Data Corruption Prevention: `transfer.fsckobjects` and related settings detect and prevent data corruption. - Other Notable Options: Blame ignore, branch sorting, color settings, editor selection, commit cleanup, core settings, diff tools, merge settings, push follow tags, rebase safety, and log date format.

Dealing with diverged git branches

Diverged branches occur when a local branch and its remote counterpart have different histories. Recognizing divergence is crucial, and ways to do so include `git status`, `git push`, or `git pull`. Resolving divergence depends on the situation. One approach is to keep both changesets by using `git pull --rebase`. To discard remote changes, use `git push --force`, but use `git push --force-with-lease` for added safety. Alternatively, to overwrite local changes, use `git reset --hard origin/main`. These solutions provide options for resolving divergence based on workflow and situation.

Inside .git

The .git directory contains essential information for version control in Git. HEAD points to the current branch. Branches, commits, trees, and blobs form the core of Git, storing commit history, file listings, and actual code, respectively. Reflogs track changes to branches, tags, and HEAD. Remote-tracking branches keep the latest commits from remote branches, while tags mark specific commits. The stash stores uncommitted changes. .git/config holds repository settings. Hooks can be used to automate actions before commits. The staging area (index) prepares files for commits. This overview provides a general understanding of the .git directory, but it does not cover all its intricacies.

Do we think of git commits as diffs, snapshots, and/or histories?

Understanding Git commits is complex, and people have varying mental models. A poll revealed 51% think of commits as diffs (changes between versions), 42% as snapshots (current state of files), and only 4% as a history of previous commits. Internally, Git stores commits as snapshots, facilitating faster checkout times. However, it also employs packfiles to compress data, storing files as deltas (differences) to save space. Despite this snapshot-based implementation, Git diffs are calculated by reconstructing snapshots from deltas and comparing them. A common "wrong" mental model is perceiving commits as diffs from the previous commit, which while inaccurate, can still be useful in daily usage. The most common mental model, considering commits as diffs, aligns with the typical focus on code changes. Other models, such as snapshots, are useful for understanding file moves and merge commits. Additionally, people may associate commits with extra information (e.g., emails, conversations) or view them as "before" and "after" states.

Some notes on NixOS

Motivated by issues with ad-hoc changes when using Ansible, the author opted to set up NixOS on a server, which provided more control over packages and users. The NixOS installation process involved using nixos-infect, copying the generated configuration, creating a flake, and deploying changes using nixos-rebuild. To run a Go service on the server, the author defined the service configuration in a single .nix file, enabling dynamic user creation and persistent storage. Despite the complexities of the Nix language syntax, the author appreciated the reliability and centralized configuration management offered by NixOS. However, questions remain regarding the specific checks performed during nixos-rebuild and a streamlined workflow for deploying service updates. Overall, the author finds NixOS promising, despite the challenges encountered in debugging and learning the language syntax.

2023: Year in review

This year, the author worked on publishing a zine explaining how integers and floats are represented in memory. They also created a tool to visualize how variables are represented in memory, called "memory spy." Additionally, the author released a playground for experimenting with integer representation called "integer.exposed." In a larger project, the author aimed to demonstrate how to implement a networking stack using Python. The first part of this project, "Implement DNS in a Weekend," was released, sparking numerous implementations in various languages. The author gave a keynote presentation on making difficult concepts accessible. They also published several blog posts and developed prototypes related to Git, including "git-commit-folders" and "git-oops." The author hired an Operations Manager to manage the logistics of their business, allowing them to focus on writing and coding. They migrated to Mastodon and have found it a more conducive platform for technical discussions. The author reflects on the challenges of completing projects, acknowledging that they often take longer than anticipated. They express gratitude for the support that enables them to pursue their unique work.

Mounting git commits as folders with NFS

- The project, "git-commit-folders," provides a novel approach to visualizing Git commits by mounting them as folders. - FUSE, NFS, and WebDAV are the supported filesystems, with NFS being the primary focus due to WebDAV's lack of symlink support. - To keep implementations synchronized, a core FS interface was created, with adapters for NFS and WebDav. - The repository's large number of commits is managed by organizing them into folders by prefix and caching packed commit hashes. - Debugging involved analyzing NFS packets with Wireshark and handling errors like "not a directory" and "stale file handle." - Inode numbers were generated by hashing file paths to avoid loops. - The "branch_histories" directory currently shows only the latest 100 commits for each branch. - Submodules are currently ignored. - NFSv4 support is available but its advantages over NFSv3 are unclear. - The project aims to make understanding Git's internal structure more intuitive by representing commits as folders.

git branches: intuition & reality

Many people intuitively perceive a git branch as an offshoot with a parent branch. However, git internally defines a branch as the complete history of every previous commit, not just the "offshoot" commits. This means that every branch contains the same full history. Internally, branches are stored as text files with the latest commit ID. While git lacks the concept of branch relationships, the intuitive model aligns with how rebases, merges, and GitHub pull requests operate. However, git's lack of hierarchy between branches and its unconventional UI for isolating offshoot commits can be confusing. GitHub's default branch has special privileges, highlighting the concept of a "special branch" despite git's hierarchical neutrality.

Some notes on nix flakes

The author, initially skeptical about Nix flakes, embarked on a journey to understand their utility, drawing parallels to Docker containers for conceptual clarity. While acknowledging flakes' superiority in reproducibility and dependency management, the author aimed to utilize them for maintaining a centralized list of system packages, seeking advantages in system setup and software uninstallation. Through a process of trial and error, the author navigated challenges like untracked files in Git repositories, incorporating unfree packages, and resolving relative path issues with flake dependencies. Using 'nix develop' and 'buildEnv', the author successfully created a directory of symlinks representing desired packages. However, the process was not without hurdles, encountering an error related to build hooks that hindered progress. Despite the difficulties, the author remained persistent in their exploration of flakes, seeking a more streamlined and manageable approach to their Nix package management workflow. The author found existing explanations of flakes difficult to grasp, relying instead on analogies and practical experimentation to develop their understanding. Although the author's initial foray into flakes presented challenges, their commitment to mastering this aspect of Nix highlights a desire for a more robust and efficient package management experience.

How git cherry-pick and revert use 3-way merge

The author, initially misunderstanding git cherry-pick as simply applying a patch, delves into its actual workings, revealing a more nuanced process involving a "3-way merge." The misconception arose when attempting to resolve merge conflicts using the patch method, which failed, unlike the expected behavior of git cherry-pick. Further investigation into git's source code revealed that cherry-pick utilizes a 3-way merge, a concept unfamiliar to the author at the time. This prompted exploration of 3-way merges, which, in essence, involve merging two files by comparing them to their original version (the base). This concept extends to applying patches in git, where the file versions before and after the commit, along with the current file, constitute the three versions used in the merge. Cherry-pick, rebase, and revert all leverage this 3-way merge strategy, differing primarily in the order and interpretation of the base and target versions. The author coins the term "3-way patch" to describe this technique, emphasizing its advantage in providing more context for merging compared to traditional patches. While git apply typically handles traditional patches, it also offers a --3way flag for performing 3-way merges. This exploration highlights the cleverness of using 3-way merges for applying patches in git, offering a unified approach for various operations while remaining transparent to users who can stick to the familiar concept of "applying a patch." The author acknowledges the complexity of merging in git, hinting at concepts like recursive merges and multiple merge algorithms, with the book "Building Git" suggested for further exploration. Finally, the author expresses appreciation for the elegant design of git's patching mechanism, praising its intuitiveness and effectiveness.