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

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.
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.
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.
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.
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.
- 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.
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.