mirror of
https://github.com/jbranchaud/til
synced 2026-07-07 01:30:32 +00:00
Compare commits
3 Commits
master
..
65b39fa25a
| Author | SHA1 | Date | |
|---|---|---|---|
| 65b39fa25a | |||
| 5615da920f | |||
| c60c63f554 |
@@ -1,5 +0,0 @@
|
|||||||
[submodule "notes"]
|
|
||||||
path = notes
|
|
||||||
url = git@github.com:jbranchaud/til-notes-private.git
|
|
||||||
branch = main
|
|
||||||
ignore = all
|
|
||||||
@@ -6,16 +6,13 @@ A collection of concise write-ups on small things I learn day to day across a
|
|||||||
variety of languages and technologies. These are things that don't really
|
variety of languages and technologies. These are things that don't really
|
||||||
warrant a full blog post. These are things I've picked up by [Learning In
|
warrant a full blog post. These are things I've picked up by [Learning In
|
||||||
Public™](https://dev.to/jbranchaud/how-i-built-a-learning-machine-45k9) and
|
Public™](https://dev.to/jbranchaud/how-i-built-a-learning-machine-45k9) and
|
||||||
working across different projects via [VisualMode](https://www.visualmode.dev/).
|
pairing with smart people at Hashrocket.
|
||||||
|
|
||||||
For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter).
|
For a steady stream of TILs, [sign up for my newsletter](https://crafty-builder-6996.ck.page/e169c61186).
|
||||||
|
|
||||||
_1811 TILs and counting..._
|
_1628 TILs and counting..._
|
||||||
|
|
||||||
See some of the other learning resources I work on:
|
See some of the other learning resources I work on:
|
||||||
|
|
||||||
- [The VisualMode Blog](https://visualmode.dev/blog)
|
|
||||||
- [Get Started with Vimium](https://egghead.io/courses/get-started-with-vimium~3t5f7)
|
|
||||||
- [Ruby Operator Lookup](https://www.visualmode.dev/ruby-operators)
|
- [Ruby Operator Lookup](https://www.visualmode.dev/ruby-operators)
|
||||||
- [Vim Un-Alphabet](https://www.youtube.com/playlist?list=PL46-cKSxMYYCMpzXo6p0Cof8hJInYgohU)
|
- [Vim Un-Alphabet](https://www.youtube.com/playlist?list=PL46-cKSxMYYCMpzXo6p0Cof8hJInYgohU)
|
||||||
|
|
||||||
@@ -30,10 +27,8 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
* [Ansible](#ansible)
|
* [Ansible](#ansible)
|
||||||
* [Astro](#astro)
|
* [Astro](#astro)
|
||||||
* [AWS](#aws)
|
* [AWS](#aws)
|
||||||
* [Bash](#bash)
|
|
||||||
* [Brew](#brew)
|
* [Brew](#brew)
|
||||||
* [Chrome](#chrome)
|
* [Chrome](#chrome)
|
||||||
* [Claude Code](#claude-code)
|
|
||||||
* [Clojure](#clojure)
|
* [Clojure](#clojure)
|
||||||
* [CSS](#css)
|
* [CSS](#css)
|
||||||
* [Deno](#deno)
|
* [Deno](#deno)
|
||||||
@@ -43,7 +38,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
* [Elixir](#elixir)
|
* [Elixir](#elixir)
|
||||||
* [Gatsby](#gatsby)
|
* [Gatsby](#gatsby)
|
||||||
* [Git](#git)
|
* [Git](#git)
|
||||||
* [GitHub](#github)
|
|
||||||
* [GitHub Actions](#github-actions)
|
* [GitHub Actions](#github-actions)
|
||||||
* [Go](#go)
|
* [Go](#go)
|
||||||
* [GROQ](#groq)
|
* [GROQ](#groq)
|
||||||
@@ -60,7 +54,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
* [Linux](#linux)
|
* [Linux](#linux)
|
||||||
* [LLM](#llm)
|
* [LLM](#llm)
|
||||||
* [Mac](#mac)
|
* [Mac](#mac)
|
||||||
* [Math](#math)
|
|
||||||
* [Mise](#mise)
|
* [Mise](#mise)
|
||||||
* [MongoDB](#mongodb)
|
* [MongoDB](#mongodb)
|
||||||
* [MySQL](#mysql)
|
* [MySQL](#mysql)
|
||||||
@@ -87,7 +80,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
* [SQLite](#sqlite)
|
* [SQLite](#sqlite)
|
||||||
* [Streaming](#streaming)
|
* [Streaming](#streaming)
|
||||||
* [Tailwind CSS](#tailwind-css)
|
* [Tailwind CSS](#tailwind-css)
|
||||||
* [Taskfile](#taskfile)
|
|
||||||
* [tmux](#tmux)
|
* [tmux](#tmux)
|
||||||
* [TypeScript](#typescript)
|
* [TypeScript](#typescript)
|
||||||
* [Unix](#unix)
|
* [Unix](#unix)
|
||||||
@@ -122,24 +114,16 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
|
|
||||||
- [AWS CLI Requires Groff Executable](aws/aws-cli-requires-groff-executable.md)
|
- [AWS CLI Requires Groff Executable](aws/aws-cli-requires-groff-executable.md)
|
||||||
- [Find And Follow Server Logs](aws/find-and-follow-server-logs.md)
|
- [Find And Follow Server Logs](aws/find-and-follow-server-logs.md)
|
||||||
- [List RDS Snapshots With Matching Identifier Prefix](aws/list-rds-snapshots-with-matching-identifier-prefix.md)
|
|
||||||
- [Output CLI Results In Different Formats](aws/output-cli-results-in-different-formats.md)
|
- [Output CLI Results In Different Formats](aws/output-cli-results-in-different-formats.md)
|
||||||
- [Sign Up User With Email And Password](aws/sign-up-user-with-email-and-password.md)
|
- [Sign Up User With Email And Password](aws/sign-up-user-with-email-and-password.md)
|
||||||
- [SSH Into An ECS Container](aws/ssh-into-an-ecs-container.md)
|
- [SSH Into An ECS Container](aws/ssh-into-an-ecs-container.md)
|
||||||
- [Turn Off Output Pager For A Command](aws/turn-off-output-pager-for-a-command.md)
|
- [Turn Off Output Pager For A Command](aws/turn-off-output-pager-for-a-command.md)
|
||||||
- [Use Specific AWS Profile With CLI](aws/use-specific-aws-profile-with-cli.md)
|
- [Use Specific AWS Profile With CLI](aws/use-specific-aws-profile-with-cli.md)
|
||||||
|
|
||||||
### Bash
|
|
||||||
|
|
||||||
- [Edit The Current Command Prompt](bash/edit-the-current-command-prompt.md)
|
|
||||||
|
|
||||||
### Brew
|
### Brew
|
||||||
|
|
||||||
- [Clean Up Your Brew Installations](brew/clean-up-your-brew-installations.md)
|
|
||||||
- [Configure Brew Environment Variables](brew/configure-brew-environment-variables.md)
|
- [Configure Brew Environment Variables](brew/configure-brew-environment-variables.md)
|
||||||
- [Export List Of Everything Installed By Brew](brew/export-list-of-everything-installed-by-brew.md)
|
- [Export List Of Everything Installed By Brew](brew/export-list-of-everything-installed-by-brew.md)
|
||||||
- [Install From Nonstandard Brewfile](brew/install-from-nonstandard-brewfile.md)
|
|
||||||
- [Install Go Packages In Brewfile](brew/install-go-packages-in-brewfile.md)
|
|
||||||
- [List All Services Managed By Brew](brew/list-all-services-managed-by-brew.md)
|
- [List All Services Managed By Brew](brew/list-all-services-managed-by-brew.md)
|
||||||
|
|
||||||
### Chrome
|
### Chrome
|
||||||
@@ -150,7 +134,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Duplicate The Current Tab](chrome/duplicate-the-current-tab.md)
|
- [Duplicate The Current Tab](chrome/duplicate-the-current-tab.md)
|
||||||
- [Easier Access To Network Throttling Controls](chrome/easier-access-to-network-throttling-controls.md)
|
- [Easier Access To Network Throttling Controls](chrome/easier-access-to-network-throttling-controls.md)
|
||||||
- [Keybinding To Focus The Address Bar](chrome/keybinding-to-focus-the-address-bar.md)
|
- [Keybinding To Focus The Address Bar](chrome/keybinding-to-focus-the-address-bar.md)
|
||||||
- [Open Current Tab In New Window With Vimium](chrome/open-current-tab-in-new-window-with-vimium.md)
|
|
||||||
- [Pause JavaScript From The Source DevTools Panel](chrome/pause-javascript-from-the-source-devtools-panel.md)
|
- [Pause JavaScript From The Source DevTools Panel](chrome/pause-javascript-from-the-source-devtools-panel.md)
|
||||||
- [Navigate The Browser History With Vimium](chrome/navigate-the-browser-history-with-vimium.md)
|
- [Navigate The Browser History With Vimium](chrome/navigate-the-browser-history-with-vimium.md)
|
||||||
- [Pretty Print Tabular Data](chrome/pretty-print-tabular-data.md)
|
- [Pretty Print Tabular Data](chrome/pretty-print-tabular-data.md)
|
||||||
@@ -163,15 +146,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Trigger Commands From The Devtools Command Palette](chrome/trigger-commands-from-the-devtools-command-palette.md)
|
- [Trigger Commands From The Devtools Command Palette](chrome/trigger-commands-from-the-devtools-command-palette.md)
|
||||||
- [View Network Traffic For New Tabs](chrome/view-network-traffic-for-new-tabs.md)
|
- [View Network Traffic For New Tabs](chrome/view-network-traffic-for-new-tabs.md)
|
||||||
|
|
||||||
### Claude Code
|
|
||||||
|
|
||||||
- [Allow Edits From The Start](claude-code/allow-edits-from-the-start.md)
|
|
||||||
- [Distinguish Sessions With Different Colors](claude-code/distinguish-sessions-with-different-colors.md)
|
|
||||||
- [Monitor Usage Limits From CLI](claude-code/monitor-usage-limits-from-cli.md)
|
|
||||||
- [Open Current Prompt In Default Editor](claude-code/open-current-prompt-in-default-editor.md)
|
|
||||||
- [Resume Specific Session](claude-code/resume-specific-session.md)
|
|
||||||
- [Stash The Current Prompt To Send Another First](claude-code/stash-the-current-prompt-to-send-another-first.md)
|
|
||||||
|
|
||||||
### Clojure
|
### Clojure
|
||||||
|
|
||||||
- [Aggregation Using merge-with](clojure/aggregation-using-merge-with.md)
|
- [Aggregation Using merge-with](clojure/aggregation-using-merge-with.md)
|
||||||
@@ -225,10 +199,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Style A Background With A Linear Gradient](css/style-a-background-with-a-linear-gradient.md)
|
- [Style A Background With A Linear Gradient](css/style-a-background-with-a-linear-gradient.md)
|
||||||
- [Using Maps In SCSS](css/using-maps-in-scss.md)
|
- [Using Maps In SCSS](css/using-maps-in-scss.md)
|
||||||
|
|
||||||
### Cursor
|
|
||||||
|
|
||||||
- [Allow Cursor To Be Launched From CLI](cursor/allow-cursor-to-be-launched-from-cli.md)
|
|
||||||
|
|
||||||
### Deno
|
### Deno
|
||||||
|
|
||||||
- [Read In The Contents Of A File](deno/read-in-the-contents-of-a-file.md)
|
- [Read In The Contents Of A File](deno/read-in-the-contents-of-a-file.md)
|
||||||
@@ -238,10 +208,9 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Aliasing An Ansible Host](devops/aliasing-an-ansible-host.md)
|
- [Aliasing An Ansible Host](devops/aliasing-an-ansible-host.md)
|
||||||
- [Allow Cross-Origin Requests To Include Cookies](devops/allow-cross-origin-requests-to-include-cookies.md)
|
- [Allow Cross-Origin Requests To Include Cookies](devops/allow-cross-origin-requests-to-include-cookies.md)
|
||||||
- [Allow HTTPS Through Your UFW Firewall](devops/allow-https-through-your-ufw-firewall.md)
|
- [Allow HTTPS Through Your UFW Firewall](devops/allow-https-through-your-ufw-firewall.md)
|
||||||
- [Check For Cached Site Assocation File For iOS](devops/check-for-cached-site-association-file-for-ios.md)
|
- [Check For Cached Site Association File For iOS](devops/check-for-cached-site-association-file-for-ios.md)
|
||||||
- [Check The Status of All Services](devops/check-the-status-of-all-services.md)
|
- [Check The Status of All Services](devops/check-the-status-of-all-services.md)
|
||||||
- [Check The Syntax Of nginx Files](devops/check-the-syntax-of-nginx-files.md)
|
- [Check The Syntax Of nginx Files](devops/check-the-syntax-of-nginx-files.md)
|
||||||
- [Cloudflare Allows CNAME For Apex Domain](devops/cloudflare-allows-cname-for-apex-domain.md)
|
|
||||||
- [Connect To An RDS PostgreSQL Database](devops/connect-to-an-rds-postgresql-database.md)
|
- [Connect To An RDS PostgreSQL Database](devops/connect-to-an-rds-postgresql-database.md)
|
||||||
- [Default Rails Deploy Script On Hatchbox](devops/default-rails-deploy-script-on-hatchbox.md)
|
- [Default Rails Deploy Script On Hatchbox](devops/default-rails-deploy-script-on-hatchbox.md)
|
||||||
- [Determine The IP Address Of A Domain](devops/determine-the-ip-address-of-a-domain.md)
|
- [Determine The IP Address Of A Domain](devops/determine-the-ip-address-of-a-domain.md)
|
||||||
@@ -251,7 +220,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Reload The nginx Configuration](devops/reload-the-nginx-configuration.md)
|
- [Reload The nginx Configuration](devops/reload-the-nginx-configuration.md)
|
||||||
- [Resolve The Public IP Of A URL](devops/resolve-the-public-ip-of-a-url.md)
|
- [Resolve The Public IP Of A URL](devops/resolve-the-public-ip-of-a-url.md)
|
||||||
- [Running Out Of inode Space](devops/running-out-of-inode-space.md)
|
- [Running Out Of inode Space](devops/running-out-of-inode-space.md)
|
||||||
- [Set, Get, And Unset Env Vars With Dokku](devops/set-get-and-unset-env-vars-with-dokku.md)
|
|
||||||
- [Set Up Domain For Hatchbox Rails App](devops/set-up-domain-for-hatchbox-rails-app.md)
|
- [Set Up Domain For Hatchbox Rails App](devops/set-up-domain-for-hatchbox-rails-app.md)
|
||||||
- [SSH Into A Docker Container](devops/ssh-into-a-docker-container.md)
|
- [SSH Into A Docker Container](devops/ssh-into-a-docker-container.md)
|
||||||
- [SSL Certificates Can Cover Multiple Domains](devops/ssl-certificates-can-cover-multiple-domains.md)
|
- [SSL Certificates Can Cover Multiple Domains](devops/ssl-certificates-can-cover-multiple-domains.md)
|
||||||
@@ -342,17 +310,13 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Caching Credentials](git/caching-credentials.md)
|
- [Caching Credentials](git/caching-credentials.md)
|
||||||
- [Change The Start Point Of A Branch](git/change-the-start-point-of-a-branch.md)
|
- [Change The Start Point Of A Branch](git/change-the-start-point-of-a-branch.md)
|
||||||
- [Check How A File Is Being Ignored](git/check-how-a-file-is-being-ignored.md)
|
- [Check How A File Is Being Ignored](git/check-how-a-file-is-being-ignored.md)
|
||||||
- [Check If A File Has Changed In A Script](git/check-if-a-file-has-changed-in-a-script.md)
|
|
||||||
- [Check If A File Is Under Version Control](git/check-if-a-file-is-under-version-control.md)
|
|
||||||
- [Checking Commit Ancestry](git/checking-commit-ancestry.md)
|
- [Checking Commit Ancestry](git/checking-commit-ancestry.md)
|
||||||
- [Checkout Old Version Of A File](git/checkout-old-version-of-a-file.md)
|
- [Checkout Old Version Of A File](git/checkout-old-version-of-a-file.md)
|
||||||
- [Checkout Previous Branch](git/checkout-previous-branch.md)
|
- [Checkout Previous Branch](git/checkout-previous-branch.md)
|
||||||
- [Cherry Pick A Range Of Commits](git/cherry-pick-a-range-of-commits.md)
|
- [Cherry Pick A Range Of Commits](git/cherry-pick-a-range-of-commits.md)
|
||||||
- [Cherry Pick Multiple Commits At Once](git/cherry-pick-multiple-commits-at-once.md)
|
|
||||||
- [Clean Out All Local Branches](git/clean-out-all-local-branches.md)
|
- [Clean Out All Local Branches](git/clean-out-all-local-branches.md)
|
||||||
- [Clean Out Working Copy With Patched Restore](git/clean-out-working-copy-with-patched-restore.md)
|
- [Clean Out Working Copy With Patched Restore](git/clean-out-working-copy-with-patched-restore.md)
|
||||||
- [Clean Up Old Remote Tracking References](git/clean-up-old-remote-tracking-references.md)
|
- [Clean Up Old Remote Tracking References](git/clean-up-old-remote-tracking-references.md)
|
||||||
- [Clear Entries From Git Stash](git/clear-entries-from-git-stash.md)
|
|
||||||
- [Clone A Repo Just For The Files, Without History](git/clone-a-repo-just-for-the-files-without-history.md)
|
- [Clone A Repo Just For The Files, Without History](git/clone-a-repo-just-for-the-files-without-history.md)
|
||||||
- [Clone A Repo Locally From .git](git/clone-a-repo-locally-from-git.md)
|
- [Clone A Repo Locally From .git](git/clone-a-repo-locally-from-git.md)
|
||||||
- [Configure Global gitignore File](git/configure-global-gitignore-file.md)
|
- [Configure Global gitignore File](git/configure-global-gitignore-file.md)
|
||||||
@@ -362,14 +326,11 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Count Number Of Commits On A Branch](git/count-number-of-commits-on-a-branch.md)
|
- [Count Number Of Commits On A Branch](git/count-number-of-commits-on-a-branch.md)
|
||||||
- [Create A New Branch With Git Switch](git/create-a-new-branch-with-git-switch.md)
|
- [Create A New Branch With Git Switch](git/create-a-new-branch-with-git-switch.md)
|
||||||
- [Delete All Untracked Files](git/delete-all-untracked-files.md)
|
- [Delete All Untracked Files](git/delete-all-untracked-files.md)
|
||||||
- [Determine Absolute Path Of Top-Level Project Directory](git/determine-absolute-path-of-top-level-project-directory.md)
|
|
||||||
- [Determine The Hash Id For A Blob](git/determine-the-hash-id-for-a-blob.md)
|
- [Determine The Hash Id For A Blob](git/determine-the-hash-id-for-a-blob.md)
|
||||||
- [Diffing With Patience](git/diffing-with-patience.md)
|
- [Diffing With Patience](git/diffing-with-patience.md)
|
||||||
- [Display All Git Log Entries In My Local Timezone](git/display-all-git-log-entries-in-my-local-timezone.md)
|
|
||||||
- [Dropping Commits With Git Rebase](git/dropping-commits-with-git-rebase.md)
|
- [Dropping Commits With Git Rebase](git/dropping-commits-with-git-rebase.md)
|
||||||
- [Dry Runs in Git](git/dry-runs-in-git.md)
|
- [Dry Runs in Git](git/dry-runs-in-git.md)
|
||||||
- [Exclude A File From A Diff Output](git/exclude-a-file-from-a-diff-output.md)
|
- [Exclude A File From A Diff Output](git/exclude-a-file-from-a-diff-output.md)
|
||||||
- [Exclude A Directory During A Command](git/exclude-a-directory-during-a-command.md)
|
|
||||||
- [Excluding Files Locally](git/excluding-files-locally.md)
|
- [Excluding Files Locally](git/excluding-files-locally.md)
|
||||||
- [Extend Git With Custom Commands](git/extend-git-with-custom-commands.md)
|
- [Extend Git With Custom Commands](git/extend-git-with-custom-commands.md)
|
||||||
- [Files With Local Changes Cannot Be Removed](git/files-with-local-changes-cannot-be-removed.md)
|
- [Files With Local Changes Cannot Be Removed](git/files-with-local-changes-cannot-be-removed.md)
|
||||||
@@ -384,7 +345,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Grep For A Pattern On Another Branch](git/grep-for-a-pattern-on-another-branch.md)
|
- [Grep For A Pattern On Another Branch](git/grep-for-a-pattern-on-another-branch.md)
|
||||||
- [Grep Over Commit Messages](git/grep-over-commit-messages.md)
|
- [Grep Over Commit Messages](git/grep-over-commit-messages.md)
|
||||||
- [Highlight Extra Whitespace In Diff Output](git/highlight-extra-whitespace-in-diff-output.md)
|
- [Highlight Extra Whitespace In Diff Output](git/highlight-extra-whitespace-in-diff-output.md)
|
||||||
- [Highlight Small Change On Single Line](git/highlight-small-change-on-single-line.md)
|
|
||||||
- [Ignore Changes To A Tracked File](git/ignore-changes-to-a-tracked-file.md)
|
- [Ignore Changes To A Tracked File](git/ignore-changes-to-a-tracked-file.md)
|
||||||
- [Ignore Files Specific To Your Workflow](git/ignore-files-specific-to-your-workflow.md)
|
- [Ignore Files Specific To Your Workflow](git/ignore-files-specific-to-your-workflow.md)
|
||||||
- [Include A Message With Your Stashed Changes](git/include-a-message-with-your-stashed-changes.md)
|
- [Include A Message With Your Stashed Changes](git/include-a-message-with-your-stashed-changes.md)
|
||||||
@@ -397,7 +357,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Last Commit A File Appeared In](git/last-commit-a-file-appeared-in.md)
|
- [Last Commit A File Appeared In](git/last-commit-a-file-appeared-in.md)
|
||||||
- [List All Files Added During Span Of Time](git/list-all-files-added-during-span-of-time.md)
|
- [List All Files Added During Span Of Time](git/list-all-files-added-during-span-of-time.md)
|
||||||
- [List All Files Changed Between Two Branches](git/list-all-files-changed-between-two-branches.md)
|
- [List All Files Changed Between Two Branches](git/list-all-files-changed-between-two-branches.md)
|
||||||
- [List All Git Aliases From gitconfig](git/list-all-git-aliases-from-gitconfig.md)
|
|
||||||
- [List Branches That Contain A Commit](git/list-branches-that-contain-a-commit.md)
|
- [List Branches That Contain A Commit](git/list-branches-that-contain-a-commit.md)
|
||||||
- [List Commits On A Branch](git/list-commits-on-a-branch.md)
|
- [List Commits On A Branch](git/list-commits-on-a-branch.md)
|
||||||
- [List Different Commits Between Two Branches](git/list-different-commits-between-two-branches.md)
|
- [List Different Commits Between Two Branches](git/list-different-commits-between-two-branches.md)
|
||||||
@@ -409,7 +368,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Move The Latest Commit To A New Branch](git/move-the-latest-commit-to-a-new-branch.md)
|
- [Move The Latest Commit To A New Branch](git/move-the-latest-commit-to-a-new-branch.md)
|
||||||
- [Override The Global Git Ignore File](git/override-the-global-git-ignore-file.md)
|
- [Override The Global Git Ignore File](git/override-the-global-git-ignore-file.md)
|
||||||
- [Pick Specific Changes To Stash](git/pick-specific-changes-to-stash.md)
|
- [Pick Specific Changes To Stash](git/pick-specific-changes-to-stash.md)
|
||||||
- [Programmatically Grab SHA For Head Commit](git/programmatically-grab-sha-for-head-commit.md)
|
|
||||||
- [Pulling In Changes During An Interactive Rebase](git/pulling-in-changes-during-an-interactive-rebase.md)
|
- [Pulling In Changes During An Interactive Rebase](git/pulling-in-changes-during-an-interactive-rebase.md)
|
||||||
- [Push To A Branch On Another Remote](git/push-to-a-branch-on-another-remote.md)
|
- [Push To A Branch On Another Remote](git/push-to-a-branch-on-another-remote.md)
|
||||||
- [Quicker Commit Fixes With The Fixup Flag](git/quicker-commit-fixes-with-the-fixup-flag.md)
|
- [Quicker Commit Fixes With The Fixup Flag](git/quicker-commit-fixes-with-the-fixup-flag.md)
|
||||||
@@ -421,12 +379,10 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Renaming A Branch](git/renaming-a-branch.md)
|
- [Renaming A Branch](git/renaming-a-branch.md)
|
||||||
- [Resetting A Reset](git/resetting-a-reset.md)
|
- [Resetting A Reset](git/resetting-a-reset.md)
|
||||||
- [Resolve A Merge Conflict From Stash Pop](git/resolve-a-merge-conflict-from-stash-pop.md)
|
- [Resolve A Merge Conflict From Stash Pop](git/resolve-a-merge-conflict-from-stash-pop.md)
|
||||||
- [Restore File From One Branch To The Current](git/restore-file-from-one-branch-to-the-current.md)
|
|
||||||
- [Review Commits From Before A Certain Date](git/review-commits-from-before-a-certain-date.md)
|
- [Review Commits From Before A Certain Date](git/review-commits-from-before-a-certain-date.md)
|
||||||
- [Run A Git Command From Outside The Repo](git/run-a-git-command-from-outside-the-repo.md)
|
- [Run A Git Command From Outside The Repo](git/run-a-git-command-from-outside-the-repo.md)
|
||||||
- [Set A Custom Pager For A Specific Command](git/set-a-custom-pager-for-a-specific-command.md)
|
- [Set A Custom Pager For A Specific Command](git/set-a-custom-pager-for-a-specific-command.md)
|
||||||
- [Set Default Branch Name For New Repos](git/set-default-branch-name-for-new-repos.md)
|
- [Set Default Branch Name For New Repos](git/set-default-branch-name-for-new-repos.md)
|
||||||
- [Set Up GPG Signing Key](git/set-up-gpg-signing-key.md)
|
|
||||||
- [Shorthand To Force Push A Branch](git/shorthand-to-force-push-a-branch.md)
|
- [Shorthand To Force Push A Branch](git/shorthand-to-force-push-a-branch.md)
|
||||||
- [Show All Commits For A File Beyond Renaming](git/show-all-commits-for-a-file-beyond-renaming.md)
|
- [Show All Commits For A File Beyond Renaming](git/show-all-commits-for-a-file-beyond-renaming.md)
|
||||||
- [Show Changes For Files That Match A Pattern](git/show-changes-for-files-that-match-a-pattern.md)
|
- [Show Changes For Files That Match A Pattern](git/show-changes-for-files-that-match-a-pattern.md)
|
||||||
@@ -434,13 +390,11 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Show File Diffs When Viewing Git Log](git/show-file-diffs-when-viewing-git-log.md)
|
- [Show File Diffs When Viewing Git Log](git/show-file-diffs-when-viewing-git-log.md)
|
||||||
- [Show List Of Most Recently Committed Branches](git/show-list-of-most-recently-committed-branches.md)
|
- [Show List Of Most Recently Committed Branches](git/show-list-of-most-recently-committed-branches.md)
|
||||||
- [Show Only Commits That Touch Specific Lines](git/show-only-commits-that-touch-specific-lines.md)
|
- [Show Only Commits That Touch Specific Lines](git/show-only-commits-that-touch-specific-lines.md)
|
||||||
- [Show Summary Stats For Current Branch](git/show-summary-stats-for-current-branch.md)
|
|
||||||
- [Show The diffstat Summary Of A Commit](git/show-the-diffstat-summary-of-a-commit.md)
|
- [Show The diffstat Summary Of A Commit](git/show-the-diffstat-summary-of-a-commit.md)
|
||||||
- [Show The Good And The Bad With Git Bisect](git/show-the-good-and-the-bad-with-git-bisect.md)
|
- [Show The Good And The Bad With Git Bisect](git/show-the-good-and-the-bad-with-git-bisect.md)
|
||||||
- [Show What Is In A Stash](git/show-what-is-in-a-stash.md)
|
- [Show What Is In A Stash](git/show-what-is-in-a-stash.md)
|
||||||
- [Single Key Presses in Interactive Mode](git/single-key-presses-in-interactive-mode.md)
|
- [Single Key Presses in Interactive Mode](git/single-key-presses-in-interactive-mode.md)
|
||||||
- [Skip A Bad Commit When Bisecting](git/skip-a-bad-commit-when-bisecting.md)
|
- [Skip A Bad Commit When Bisecting](git/skip-a-bad-commit-when-bisecting.md)
|
||||||
- [Skip Git Hooks As Needed](git/skip-git-hooks-as-needed.md)
|
|
||||||
- [Skip Pre-Commit Hooks](git/skip-pre-commit-hooks.md)
|
- [Skip Pre-Commit Hooks](git/skip-pre-commit-hooks.md)
|
||||||
- [Staging Changes Within Vim](git/staging-changes-within-vim.md)
|
- [Staging Changes Within Vim](git/staging-changes-within-vim.md)
|
||||||
- [Staging Stashes Interactively](git/staging-stashes-interactively.md)
|
- [Staging Stashes Interactively](git/staging-stashes-interactively.md)
|
||||||
@@ -452,7 +406,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Transition A Branch From One Base To Another](git/transition-a-branch-from-one-base-to-another.md)
|
- [Transition A Branch From One Base To Another](git/transition-a-branch-from-one-base-to-another.md)
|
||||||
- [Turn Off The Output Pager For One Command](git/turn-off-the-output-pager-for-one-command.md)
|
- [Turn Off The Output Pager For One Command](git/turn-off-the-output-pager-for-one-command.md)
|
||||||
- [Two Kinds Of Dotted Range Notation](git/two-kinds-of-dotted-range-notation.md)
|
- [Two Kinds Of Dotted Range Notation](git/two-kinds-of-dotted-range-notation.md)
|
||||||
- [Undo Latest Changes Committed To Specific File](git/undo-latest-changes-committed-to-specific-file.md)
|
|
||||||
- [Unstage Changes Wih Git Restore](git/unstage-changes-with-git-restore.md)
|
- [Unstage Changes Wih Git Restore](git/unstage-changes-with-git-restore.md)
|
||||||
- [Untrack A Directory Of Files Without Deleting](git/untrack-a-directory-of-files-without-deleting.md)
|
- [Untrack A Directory Of Files Without Deleting](git/untrack-a-directory-of-files-without-deleting.md)
|
||||||
- [Untrack A File Without Deleting It](git/untrack-a-file-without-deleting-it.md)
|
- [Untrack A File Without Deleting It](git/untrack-a-file-without-deleting-it.md)
|
||||||
@@ -465,16 +418,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [What Is The Current Branch?](git/what-is-the-current-branch.md)
|
- [What Is The Current Branch?](git/what-is-the-current-branch.md)
|
||||||
- [Whitespace Warnings](git/whitespace-warnings.md)
|
- [Whitespace Warnings](git/whitespace-warnings.md)
|
||||||
|
|
||||||
### GitHub
|
|
||||||
|
|
||||||
- [Access Your GitHub Profile Photo](github/access-your-github-profile-photo.md)
|
|
||||||
- [List PRs Awaiting Your Review](github/list-prs-awaiting-your-review.md)
|
|
||||||
- [Open A PR To An Unforked Repo](github/open-a-pr-to-an-unforked-repo.md)
|
|
||||||
- [Open File To Specific Line In Browser](github/open-file-to-specific-line-in-browser.md)
|
|
||||||
- [Process JSON Output From gh With jq](github/process-json-output-from-gh-with-jq.md)
|
|
||||||
- [Target Another Repo When Creating A PR](github/target-another-repo-when-creating-a-pr.md)
|
|
||||||
- [Tell gh What The Default Repo Is](github/tell-gh-what-the-default-repo-is.md)
|
|
||||||
|
|
||||||
### GitHub Actions
|
### GitHub Actions
|
||||||
|
|
||||||
- [Cache Playwright Dependencies Across Workflows](github-actions/cache-playwright-dependencies-across-workflows.md)
|
- [Cache Playwright Dependencies Across Workflows](github-actions/cache-playwright-dependencies-across-workflows.md)
|
||||||
@@ -522,20 +465,17 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
|
|
||||||
### Heroku
|
### Heroku
|
||||||
|
|
||||||
- [Check Ruby Version For Production App](heroku/check-ruby-version-for-production-app.md)
|
|
||||||
- [Connect To A Database By Color](heroku/connect-to-a-database-by-color.md)
|
- [Connect To A Database By Color](heroku/connect-to-a-database-by-color.md)
|
||||||
- [Deploy A Review App To A Different Stack](heroku/deploy-a-review-app-to-a-different-stack.md)
|
- [Deploy A Review App To A Different Stack](heroku/deploy-a-review-app-to-a-different-stack.md)
|
||||||
- [Diagnose Problems In A Heroku Postgres Database](heroku/diagnose-problems-in-a-heroku-postgres-database.md)
|
- [Diagnose Problems In A Heroku Postgres Database](heroku/diagnose-problems-in-a-heroku-postgres-database.md)
|
||||||
- [Open Dashboard For Specific Add-On](heroku/open-dashboard-for-specific-add-on.md)
|
- [Open Dashboard For Specific Add-On](heroku/open-dashboard-for-specific-add-on.md)
|
||||||
- [Run SQL Against Remote Postgres Database](heroku/run-sql-against-remote-postgres-database.md)
|
- [Run SQL Against Remote Postgres Database](heroku/run-sql-against-remote-postgres-database.md)
|
||||||
- [Set And Show Heroku Env Variables](heroku/set-and-show-heroku-env-variables.md)
|
- [Set And Show Heroku Env Variables](heroku/set-and-show-heroku-env-variables.md)
|
||||||
- [Specify Default Team And App For Project](heroku/specify-default-team-and-app-for-project.md)
|
|
||||||
- [SSH Into Heroku Server Hosting App](heroku/ssh-into-heroku-server-hosting-app.md)
|
- [SSH Into Heroku Server Hosting App](heroku/ssh-into-heroku-server-hosting-app.md)
|
||||||
|
|
||||||
### HTML
|
### HTML
|
||||||
|
|
||||||
- [Adding Alt Text To An Image](html/adding-alt-text-to-an-image.md)
|
- [Adding Alt Text To An Image](html/adding-alt-text-to-an-image.md)
|
||||||
- [Allow Number Input To Accept Decimal Values](html/allow-number-input-to-accept-decimal-values.md)
|
|
||||||
- [Determine Which Button Submitted The Form](html/determine-which-button-submitted-the-form.md)
|
- [Determine Which Button Submitted The Form](html/determine-which-button-submitted-the-form.md)
|
||||||
- [Disable Auto-Completion For A Form Input](html/disable-auto-completion-for-a-form-input.md)
|
- [Disable Auto-Completion For A Form Input](html/disable-auto-completion-for-a-form-input.md)
|
||||||
- [Disclose Additional Details](html/disclose-additional-details.md)
|
- [Disclose Additional Details](html/disclose-additional-details.md)
|
||||||
@@ -568,7 +508,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Focus The URL Bar](internet/focus-the-url-bar.md)
|
- [Focus The URL Bar](internet/focus-the-url-bar.md)
|
||||||
- [Get Random Images From Unsplash](internet/get-random-images-from-unsplash.md)
|
- [Get Random Images From Unsplash](internet/get-random-images-from-unsplash.md)
|
||||||
- [Grab The RSS Feed For A Substack Blog](internet/grab-the-rss-feed-for-a-substack-blog.md)
|
- [Grab The RSS Feed For A Substack Blog](internet/grab-the-rss-feed-for-a-substack-blog.md)
|
||||||
- [Hide Overflowing Text For Google Sheets Column](internet/hide-overflowing-text-for-google-sheets-column.md)
|
|
||||||
- [Search Tweets By Author](internet/search-tweets-by-author.md)
|
- [Search Tweets By Author](internet/search-tweets-by-author.md)
|
||||||
- [Show All Pivotal Stories With Blockers](internet/show-all-pivotal-stories-with-blockers.md)
|
- [Show All Pivotal Stories With Blockers](internet/show-all-pivotal-stories-with-blockers.md)
|
||||||
- [Verify Site Ownership With DNS Record](internet/verify-site-ownership-with-dns-record.md)
|
- [Verify Site Ownership With DNS Record](internet/verify-site-ownership-with-dns-record.md)
|
||||||
@@ -615,7 +554,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Find Where Yarn Is Installing Binaries](javascript/find-where-yarn-is-installing-binaries.md)
|
- [Find Where Yarn Is Installing Binaries](javascript/find-where-yarn-is-installing-binaries.md)
|
||||||
- [for...in Iterates Over Object Properties](javascript/for-in-iterates-over-object-properties.md)
|
- [for...in Iterates Over Object Properties](javascript/for-in-iterates-over-object-properties.md)
|
||||||
- [Format A Decimal To A Fixed Number Of Digits](javascript/format-a-decimal-to-a-fixed-number-of-digits.md)
|
- [Format A Decimal To A Fixed Number Of Digits](javascript/format-a-decimal-to-a-fixed-number-of-digits.md)
|
||||||
- [Format A List Of Items By Locale](javascript/format-a-list-of-items-by-locale.md)
|
|
||||||
- [Format Time Zone Identifier](javascript/format-time-zone-identifier.md)
|
- [Format Time Zone Identifier](javascript/format-time-zone-identifier.md)
|
||||||
- [Formatting Values With Units For Display](javascript/formatting-values-with-units-for-display.md)
|
- [Formatting Values With Units For Display](javascript/formatting-values-with-units-for-display.md)
|
||||||
- [Freeze An Object, Sorta](javascript/freeze-an-object-sorta.md)
|
- [Freeze An Object, Sorta](javascript/freeze-an-object-sorta.md)
|
||||||
@@ -625,7 +563,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Get The Response Status From An Axios Error](javascript/get-the-response-status-from-an-axios-error.md)
|
- [Get The Response Status From An Axios Error](javascript/get-the-response-status-from-an-axios-error.md)
|
||||||
- [Get The Time Components Of A Date](javascript/get-the-time-components-of-a-date.md)
|
- [Get The Time Components Of A Date](javascript/get-the-time-components-of-a-date.md)
|
||||||
- [Get The Time Zone Of The Client Computer](javascript/get-the-time-zone-of-the-client-computer.md)
|
- [Get The Time Zone Of The Client Computer](javascript/get-the-time-zone-of-the-client-computer.md)
|
||||||
- [Get User's Preferred Language From Browser](javascript/get-users-preferred-language-from-browser.md)
|
|
||||||
- [Globally Install A Package With Yarn](javascript/globally-install-a-package-with-yarn.md)
|
- [Globally Install A Package With Yarn](javascript/globally-install-a-package-with-yarn.md)
|
||||||
- [Globally Install Specific Version Of PNPM](javascript/globally-install-specific-version-of-pnpm.md)
|
- [Globally Install Specific Version Of PNPM](javascript/globally-install-specific-version-of-pnpm.md)
|
||||||
- [Immutable Remove With The Spread Operator](javascript/immutable-remove-with-the-spread-operator.md)
|
- [Immutable Remove With The Spread Operator](javascript/immutable-remove-with-the-spread-operator.md)
|
||||||
@@ -690,9 +627,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
### jj
|
### jj
|
||||||
|
|
||||||
- [Colocate jj And git Directories For Project](jj/colocate-jj-and-git-directories-for-project.md)
|
- [Colocate jj And git Directories For Project](jj/colocate-jj-and-git-directories-for-project.md)
|
||||||
- [Describe Current Changes And Create New Change](jj/describe-current-changes-and-create-new-change.md)
|
|
||||||
- [Find System-wide Config File For User](jj/find-system-wide-config-file-for-user.md)
|
- [Find System-wide Config File For User](jj/find-system-wide-config-file-for-user.md)
|
||||||
- [Squash Changes Into Parent Commit Interactively](jj/squash-changes-into-parent-commit-interactively.md)
|
|
||||||
|
|
||||||
### jq
|
### jq
|
||||||
|
|
||||||
@@ -726,7 +661,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
|
|
||||||
### LLM
|
### LLM
|
||||||
|
|
||||||
- [Count Number Of Tokens In A File](llm/count-number-of-tokens-in-a-file.md)
|
|
||||||
- [Send cURL To Claude Text Completion API](llm/send-curl-to-claude-text-completion-api.md)
|
- [Send cURL To Claude Text Completion API](llm/send-curl-to-claude-text-completion-api.md)
|
||||||
- [Use The llm CLI With Claude Models](llm/use-the-llm-cli-with-claude-models.md)
|
- [Use The llm CLI With Claude Models](llm/use-the-llm-cli-with-claude-models.md)
|
||||||
|
|
||||||
@@ -735,32 +669,22 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Access All Screen And Video Capture Options](mac/access-all-screen-and-video-capture-options.md)
|
- [Access All Screen And Video Capture Options](mac/access-all-screen-and-video-capture-options.md)
|
||||||
- [Access System Information On OS X](mac/access-system-information-on-osx.md)
|
- [Access System Information On OS X](mac/access-system-information-on-osx.md)
|
||||||
- [Access Unsupported Screen Resolutions With RDM](mac/access-unsupported-screen-resolutions-with-rdm.md)
|
- [Access Unsupported Screen Resolutions With RDM](mac/access-unsupported-screen-resolutions-with-rdm.md)
|
||||||
- [Add A Bunch Of CLI Utilities With coreutils](mac/add-a-bunch-of-cli-utilities-with-coreutils.md)
|
|
||||||
- [Capture Screenshot To Clipboard From CLI](mac/capture-screenshot-to-clipboard-from-cli.md)
|
|
||||||
- [Check Network Quality Stats From The Command Line](mac/check-network-quality-stats-from-the-command-line.md)
|
- [Check Network Quality Stats From The Command Line](mac/check-network-quality-stats-from-the-command-line.md)
|
||||||
- [Clean Up Item Layout In Finder Window](mac/clean-up-item-layout-in-finder-window.md)
|
|
||||||
- [Clean Up Old Homebrew Files](mac/clean-up-old-homebrew-files.md)
|
- [Clean Up Old Homebrew Files](mac/clean-up-old-homebrew-files.md)
|
||||||
- [Control Which Monitor App Switcher Appears On](mac/control-which-monitor-app-switcher-appears-on.md)
|
|
||||||
- [Convert An HEIC Image File To JPG](mac/convert-an-heic-image-file-to-jpg.md)
|
- [Convert An HEIC Image File To JPG](mac/convert-an-heic-image-file-to-jpg.md)
|
||||||
- [Default Screenshot Location](mac/default-screenshot-location.md)
|
- [Default Screenshot Location](mac/default-screenshot-location.md)
|
||||||
- [Detect How Long A User Has Been Idle](mac/detect-how-long-a-user-has-been-idle.md)
|
|
||||||
- [Disable Swipe Navigation For A Specific App](mac/disable-swipe-navigation-for-a-specific-app.md)
|
- [Disable Swipe Navigation For A Specific App](mac/disable-swipe-navigation-for-a-specific-app.md)
|
||||||
- [Display A Message With Alfred](mac/display-a-message-with-alfred.md)
|
- [Display A Message With Alfred](mac/display-a-message-with-alfred.md)
|
||||||
- [Find The Process Using A Specific Port](mac/find-the-process-using-a-specific-port.md)
|
- [Find The Process Using A Specific Port](mac/find-the-process-using-a-specific-port.md)
|
||||||
- [Gesture For Viewing All Windows Of Current App](mac/gesture-for-viewing-all-windows-of-current-app.md)
|
- [Gesture For Viewing All Windows Of Current App](mac/gesture-for-viewing-all-windows-of-current-app.md)
|
||||||
- [Insert A Non-Breaking Space Character](mac/insert-a-non-breaking-space-character.md)
|
- [Insert A Non-Breaking Space Character](mac/insert-a-non-breaking-space-character.md)
|
||||||
- [Inspect Assertions Preventing Sleep](mac/inspect-assertions-preventing-sleep.md)
|
|
||||||
- [Keyboard Shortcuts For Interesting With Text Areas](mac/keyboard-shortcuts-for-interacting-with-text-areas.md)
|
- [Keyboard Shortcuts For Interesting With Text Areas](mac/keyboard-shortcuts-for-interacting-with-text-areas.md)
|
||||||
- [Launch Some Confetti](mac/launch-some-confetti.md)
|
|
||||||
- [List All The Say Voices](mac/list-all-the-say-voices.md)
|
- [List All The Say Voices](mac/list-all-the-say-voices.md)
|
||||||
- [Open Finder.app To Specific Directory](mac/open-finder-app-to-specific-directory.md)
|
- [Open Finder.app To Specific Directory](mac/open-finder-app-to-specific-directory.md)
|
||||||
- [Prevent Sleep With The Caffeinate Command](mac/prevent-sleep-with-the-caffeinate-command.md)
|
|
||||||
- [Quickly Type En Dashes And Em Dashes](mac/quickly-type-en-dashes-and-em-dashes.md)
|
- [Quickly Type En Dashes And Em Dashes](mac/quickly-type-en-dashes-and-em-dashes.md)
|
||||||
- [Read The Lid Angle Sensor For A MacBook](mac/read-the-lid-angle-sensor-for-a-macbook.md)
|
|
||||||
- [Require Additional JS Libraries In Postman](mac/require-additional-js-libraries-in-postman.md)
|
- [Require Additional JS Libraries In Postman](mac/require-additional-js-libraries-in-postman.md)
|
||||||
- [Resize App Windows With AppleScript](mac/resize-app-windows-with-applescript.md)
|
- [Resize App Windows With AppleScript](mac/resize-app-windows-with-applescript.md)
|
||||||
- [Resizing Both Corners Of A Window](mac/resizing-both-corners-of-a-window.md)
|
- [Resizing Both Corners Of A Window](mac/resizing-both-corners-of-a-window.md)
|
||||||
- [Reveal Location Of File In Finder.app](mac/reveal-location-of-file-in-finder-app.md)
|
|
||||||
- [Run A Hardware Check](mac/run-a-hardware-check.md)
|
- [Run A Hardware Check](mac/run-a-hardware-check.md)
|
||||||
- [Run AppleScript Commands Inline In The Terminal](mac/run-applescript-commands-inline-in-the-terminal.md)
|
- [Run AppleScript Commands Inline In The Terminal](mac/run-applescript-commands-inline-in-the-terminal.md)
|
||||||
- [Set A Window To Its Default Zoom Level](mac/set-a-window-to-its-default-zoom-level.md)
|
- [Set A Window To Its Default Zoom Level](mac/set-a-window-to-its-default-zoom-level.md)
|
||||||
@@ -772,21 +696,13 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [View All Windows Of The Current App](mac/view-all-windows-of-the-current-app.md)
|
- [View All Windows Of The Current App](mac/view-all-windows-of-the-current-app.md)
|
||||||
- [Write System Clipboard To A File](mac/write-system-clipboard-to-a-file.md)
|
- [Write System Clipboard To A File](mac/write-system-clipboard-to-a-file.md)
|
||||||
|
|
||||||
### Math
|
|
||||||
|
|
||||||
- [Generate Permutations Of All Valid 9-ball Racks](math/generate-permutations-of-all-valid-9-ball-racks.md)
|
|
||||||
|
|
||||||
### Mise
|
### Mise
|
||||||
|
|
||||||
- [Create Umbrella Task For All Test Tasks](mise/create-umbrella-task-for-all-test-tasks.md)
|
- [Create Umbrella Task For All Test Tasks](mise/create-umbrella-task-for-all-test-tasks.md)
|
||||||
- [List The Files Being Loaded By Mise](mise/list-the-files-being-loaded-by-mise.md)
|
- [List The Files Being Loaded By Mise](mise/list-the-files-being-loaded-by-mise.md)
|
||||||
- [Look In Ruby Version Dotfile](mise/look-in-ruby-version-dotfile.md)
|
|
||||||
- [Override Your Project Mise File](mise/override-your-project-mise-file.md)
|
|
||||||
- [Pick From Tasks Using Interactive Picker](mise/pick-from-tasks-using-interactive-picker.md)
|
|
||||||
- [Preserve Color Output For Task Command](mise/preserve-color-output-for-task-command.md)
|
- [Preserve Color Output For Task Command](mise/preserve-color-output-for-task-command.md)
|
||||||
- [Read Existing Dot Env File Into Env Vars](mise/read-existing-dot-env-file-into-env-vars.md)
|
- [Read Existing Dot Env File Into Env Vars](mise/read-existing-dot-env-file-into-env-vars.md)
|
||||||
- [Run A Command With Specific Tool Version](mise/run-a-command-with-specific-tool-version.md)
|
- [Run A Command With Specific Tool Version](mise/run-a-command-with-specific-tool-version.md)
|
||||||
- [Search Through Bin Paths For Tool Locations](mise/search-through-bin-paths-for-tool-locations.md)
|
|
||||||
|
|
||||||
### MongoDB
|
### MongoDB
|
||||||
|
|
||||||
@@ -805,7 +721,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Doing Date Math](mysql/doing-date-math.md)
|
- [Doing Date Math](mysql/doing-date-math.md)
|
||||||
- [Dump A Database To A File](mysql/dump-a-database-to-a-file.md)
|
- [Dump A Database To A File](mysql/dump-a-database-to-a-file.md)
|
||||||
- [Echo A Message From A SQL File](mysql/echo-a-message-from-a-sql-file.md)
|
- [Echo A Message From A SQL File](mysql/echo-a-message-from-a-sql-file.md)
|
||||||
- [Get Idea Of What Is In A JSON Column](mysql/get-idea-of-what-is-in-a-json-column.md)
|
|
||||||
- [Ignore Duplicates When Inserting Records](mysql/ignore-duplicates-when-inserting-records.md)
|
- [Ignore Duplicates When Inserting Records](mysql/ignore-duplicates-when-inserting-records.md)
|
||||||
- [List Databases And Tables](mysql/list-databases-and-tables.md)
|
- [List Databases And Tables](mysql/list-databases-and-tables.md)
|
||||||
- [Run Statements In A Transaction](mysql/run-statements-in-a-transaction.md)
|
- [Run Statements In A Transaction](mysql/run-statements-in-a-transaction.md)
|
||||||
@@ -819,9 +734,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
|
|
||||||
- [Allow Neovim To Copy/Paste With System Clipboard](neovim/allow-neovim-to-copy-paste-with-system-clipboard.md)
|
- [Allow Neovim To Copy/Paste With System Clipboard](neovim/allow-neovim-to-copy-paste-with-system-clipboard.md)
|
||||||
- [Create User Command To Open Init Config](neovim/create-user-command-to-open-init-config.md)
|
- [Create User Command To Open Init Config](neovim/create-user-command-to-open-init-config.md)
|
||||||
- [Jump Between Changes In Current File](neovim/jump-between-changes-in-current-file.md)
|
|
||||||
- [Run A Lua Statement From The Command Prompt](neovim/run-a-lua-statement-from-the-command-prompt.md)
|
- [Run A Lua Statement From The Command Prompt](neovim/run-a-lua-statement-from-the-command-prompt.md)
|
||||||
- [Run nvim With Factory Defaults](neovim/run-nvim-with-factory-defaults.md)
|
|
||||||
- [Set Up Vim-Plug With Neovim](neovim/set-up-vim-plug-with-neovim.md)
|
- [Set Up Vim-Plug With Neovim](neovim/set-up-vim-plug-with-neovim.md)
|
||||||
|
|
||||||
### Netlify
|
### Netlify
|
||||||
@@ -859,12 +772,10 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
|
|
||||||
### Planetscale
|
### Planetscale
|
||||||
|
|
||||||
- [See What Databases You Have Access To](planetscale/see-what-databases-you-have-access-to.md)
|
|
||||||
- [Seed Production Data Into Another Branch](planetscale/seed-production-data-into-another-branch.md)
|
- [Seed Production Data Into Another Branch](planetscale/seed-production-data-into-another-branch.md)
|
||||||
|
|
||||||
### pnpm
|
### pnpm
|
||||||
|
|
||||||
- [Avoid Vulnerabilities In New Package Versions](pnpm/avoid-vulnerabilities-in-new-package-versions.md)
|
|
||||||
- [Execute A Command From The Workspace Root](pnpm/execute-a-command-from-the-workspace-root.md)
|
- [Execute A Command From The Workspace Root](pnpm/execute-a-command-from-the-workspace-root.md)
|
||||||
- [Install Command Runs For Entire Workspace](pnpm/install-command-runs-for-entire-workspace.md)
|
- [Install Command Runs For Entire Workspace](pnpm/install-command-runs-for-entire-workspace.md)
|
||||||
- [List The Installed Version Of A Specific Package](pnpm/list-the-installed-version-of-a-specific-package.md)
|
- [List The Installed Version Of A Specific Package](pnpm/list-the-installed-version-of-a-specific-package.md)
|
||||||
@@ -886,14 +797,12 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Check If Clusters Are Upgrade Compatible](postgres/check-if-clusters-are-upgrade-compatible.md)
|
- [Check If Clusters Are Upgrade Compatible](postgres/check-if-clusters-are-upgrade-compatible.md)
|
||||||
- [Check If The Local Server Is Running](postgres/check-if-the-local-server-is-running.md)
|
- [Check If The Local Server Is Running](postgres/check-if-the-local-server-is-running.md)
|
||||||
- [Check If User Role Exists For Database](postgres/check-if-user-role-exists-for-database.md)
|
- [Check If User Role Exists For Database](postgres/check-if-user-role-exists-for-database.md)
|
||||||
- [Check Table For Any Oprhaned Records](postgres/check-table-for-any-orphaned-records.md)
|
- [Check Table For Any Orphaned Records](postgres/check-table-for-any-orphaned-records.md)
|
||||||
- [Check The Size Of Databases In A Cluster](postgres/check-the-size-of-databases-in-a-cluster.md)
|
|
||||||
- [Checking Inequality](postgres/checking-inequality.md)
|
- [Checking Inequality](postgres/checking-inequality.md)
|
||||||
- [Checking The Type Of A Value](postgres/checking-the-type-of-a-value.md)
|
- [Checking The Type Of A Value](postgres/checking-the-type-of-a-value.md)
|
||||||
- [Clear The Screen In psql](postgres/clear-the-screen-in-psql.md)
|
- [Clear The Screen In psql](postgres/clear-the-screen-in-psql.md)
|
||||||
- [Clear The Screen In psql (2)](postgres/clear-the-screen-in-psql-2.md)
|
- [Clear The Screen In psql (2)](postgres/clear-the-screen-in-psql-2.md)
|
||||||
- [Compute Hashes With pgcrypto](postgres/compute-hashes-with-pgcrypto.md)
|
- [Compute Hashes With pgcrypto](postgres/compute-hashes-with-pgcrypto.md)
|
||||||
- [Compute Median Instead Of Average](postgres/compute-median-instead-of-average.md)
|
|
||||||
- [Compute The Levenshtein Distance Of Two Strings](postgres/compute-the-levenshtein-distance-of-two-strings.md)
|
- [Compute The Levenshtein Distance Of Two Strings](postgres/compute-the-levenshtein-distance-of-two-strings.md)
|
||||||
- [Compute The md5 Hash Of A String](postgres/compute-the-md5-hash-of-a-string.md)
|
- [Compute The md5 Hash Of A String](postgres/compute-the-md5-hash-of-a-string.md)
|
||||||
- [Concatenate Strings With A Separator](postgres/concatenate-strings-with-a-separator.md)
|
- [Concatenate Strings With A Separator](postgres/concatenate-strings-with-a-separator.md)
|
||||||
@@ -909,7 +818,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Create A Table From The Structure Of Another](postgres/create-a-table-from-the-structure-of-another.md)
|
- [Create A Table From The Structure Of Another](postgres/create-a-table-from-the-structure-of-another.md)
|
||||||
- [Create An Index Across Two Columns](postgres/create-an-index-across-two-columns.md)
|
- [Create An Index Across Two Columns](postgres/create-an-index-across-two-columns.md)
|
||||||
- [Create An Index Without Locking The Table](postgres/create-an-index-without-locking-the-table.md)
|
- [Create An Index Without Locking The Table](postgres/create-an-index-without-locking-the-table.md)
|
||||||
- [Create And Execute SQL Statements With \gexec](postgres/create-and-execute-sql-statements-with-gexec.md)
|
|
||||||
- [Create Database Uses Template1](postgres/create-database-uses-template1.md)
|
- [Create Database Uses Template1](postgres/create-database-uses-template1.md)
|
||||||
- [Create hstore From Two Arrays](postgres/create-hstore-from-two-arrays.md)
|
- [Create hstore From Two Arrays](postgres/create-hstore-from-two-arrays.md)
|
||||||
- [Create Table Adds A Data Type](postgres/create-table-adds-a-data-type.md)
|
- [Create Table Adds A Data Type](postgres/create-table-adds-a-data-type.md)
|
||||||
@@ -993,7 +901,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Prevent A Query From Running Too Long](postgres/prevent-a-query-from-running-too-long.md)
|
- [Prevent A Query From Running Too Long](postgres/prevent-a-query-from-running-too-long.md)
|
||||||
- [Print The Query Buffer In psql](postgres/print-the-query-buffer-in-psql.md)
|
- [Print The Query Buffer In psql](postgres/print-the-query-buffer-in-psql.md)
|
||||||
- [Put Unique Constraint On Generated Column](postgres/put-unique-constraint-on-generated-column.md)
|
- [Put Unique Constraint On Generated Column](postgres/put-unique-constraint-on-generated-column.md)
|
||||||
- [References Target Primary Key By Default](postgres/references-target-primary-key-by-default.md)
|
|
||||||
- [Remove Not Null Constraint From A Column](postgres/remove-not-null-constraint-from-a-column.md)
|
- [Remove Not Null Constraint From A Column](postgres/remove-not-null-constraint-from-a-column.md)
|
||||||
- [Renaming A Sequence](postgres/renaming-a-sequence.md)
|
- [Renaming A Sequence](postgres/renaming-a-sequence.md)
|
||||||
- [Renaming A Table](postgres/renaming-a-table.md)
|
- [Renaming A Table](postgres/renaming-a-table.md)
|
||||||
@@ -1004,7 +911,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Set Inclusion With hstore](postgres/set-inclusion-with-hstore.md)
|
- [Set Inclusion With hstore](postgres/set-inclusion-with-hstore.md)
|
||||||
- [Set A Seed For The Random Number Generator](postgres/set-a-seed-for-the-random-number-generator.md)
|
- [Set A Seed For The Random Number Generator](postgres/set-a-seed-for-the-random-number-generator.md)
|
||||||
- [Set A Statement Timeout Threshold For A Session](postgres/set-a-statement-timeout-threshold-for-a-session.md)
|
- [Set A Statement Timeout Threshold For A Session](postgres/set-a-statement-timeout-threshold-for-a-session.md)
|
||||||
- [Set Up A Project-Local Cluster With Postgres.app](postgres/set-up-a-project-local-cluster-with-postgres-app.md)
|
|
||||||
- [Sets With The Values Command](postgres/sets-with-the-values-command.md)
|
- [Sets With The Values Command](postgres/sets-with-the-values-command.md)
|
||||||
- [Shorthand Absolute Value Operator](postgres/shorthand-absolute-value-operator.md)
|
- [Shorthand Absolute Value Operator](postgres/shorthand-absolute-value-operator.md)
|
||||||
- [Show All Versions Of An Operator](postgres/show-all-versions-of-an-operator.md)
|
- [Show All Versions Of An Operator](postgres/show-all-versions-of-an-operator.md)
|
||||||
@@ -1061,49 +967,13 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
### Python
|
### Python
|
||||||
|
|
||||||
- [Access Instance Variables](python/access-instance-variables.md)
|
- [Access Instance Variables](python/access-instance-variables.md)
|
||||||
- [Access Most Recent Return Value In REPL](python/access-most-recent-return-value-in-repl.md)
|
|
||||||
- [Access Variables Outside Loop Scope](python/access-variables-outside-loop-scope.md)
|
|
||||||
- [Argument Defaults Are Evaluated When Function Is Defined](python/argument-defaults-are-evaluated-when-function-is-defined.md)
|
|
||||||
- [Assert Is Only A Development Check](python/assert-is-only-a-development-check.md)
|
|
||||||
- [Avoid Modification With Frozen Dataclass](python/avoid-modification-with-frozen-dataclass.md)
|
|
||||||
- [Break Debugger On First Line Of Program](python/break-debugger-on-first-line-of-program.md)
|
- [Break Debugger On First Line Of Program](python/break-debugger-on-first-line-of-program.md)
|
||||||
- [Check If Package Is Installed With Pip](python/check-if-package-is-installed-with-pip.md)
|
|
||||||
- [Check Precondition Before Click Arg Parsing](python/check-precondition-before-click-arg-parsing.md)
|
|
||||||
- [Control Passing Of Time In Tests](python/control-passing-of-time-in-tests.md)
|
|
||||||
- [Create A Dummy DataFrame In Pandas](python/create-a-dummy-dataframe-in-pandas.md)
|
- [Create A Dummy DataFrame In Pandas](python/create-a-dummy-dataframe-in-pandas.md)
|
||||||
- [Create A Range Of Descending Values](python/create-a-range-of-descending-values.md)
|
|
||||||
- [Deduplicate A List Into A Tuple](python/deduplicate-a-list-into-a-tuple.md)
|
|
||||||
- [Define Sequence Of Tests With Parametrize Decorator](python/define-sequence-of-tests-with-parametrize-decorator.md)
|
|
||||||
- [Define Typed Class Interface With Protocol](python/define-typed-class-interface-with-protocol.md)
|
|
||||||
- [Dunder Methods](python/dunder-methods.md)
|
- [Dunder Methods](python/dunder-methods.md)
|
||||||
- [Easy Key-Value Aggregates With defaultdict](python/easy-key-value-aggregates-with-defaultdict.md)
|
|
||||||
- [Enable Pyright Type Checking In Cursor](python/enable-pyright-type-checking-in-cursor.md)
|
|
||||||
- [Get Absolute Seconds From `timedelta` Object](python/get-absolute-seconds-from-timedelta-object.md)
|
|
||||||
- [Get Quotient And Remainder In One Operation](python/get-quotient-and-remainder-in-one-operation.md)
|
|
||||||
- [Install With PIP For Specific Interpreter](python/install-with-pip-for-specific-interpreter.md)
|
|
||||||
- [Iterate First N Items From Enumerable](python/iterate-first-n-items-from-enumerable.md)
|
|
||||||
- [Iterate Over A Dictionary](python/iterate-over-a-dictionary.md)
|
|
||||||
- [Keep A Tally With collections.Counter](python/keep-a-tally-with-collections-counter.md)
|
|
||||||
- [Lint And Format Project With Ruff](python/lint-and-format-project-with-ruff.md)
|
|
||||||
- [Load A File Into The Python REPL](python/load-a-file-into-the-python-repl.md)
|
|
||||||
- [Look Inside Pytest tmp_path](python/look-inside-pytest-tmp-path.md)
|
|
||||||
- [Make Dataclass Sortable By Specific Field](python/make-dataclass-sortable-by-specific-field.md)
|
|
||||||
- [Make Secure Temp File For Atomic Write](python/make-secure-temp-file-for-atomic-write.md)
|
|
||||||
- [Override The Boolean Context Of A Class](python/override-the-boolean-context-of-a-class.md)
|
- [Override The Boolean Context Of A Class](python/override-the-boolean-context-of-a-class.md)
|
||||||
- [Parse Relative Time To datetime Object](python/parse-relative-time-to-datetime-object.md)
|
|
||||||
- [Reclassify Certain Packages As Dev Dependencies](python/reclassify-certain-packages-as-dev-dependencies.md)
|
|
||||||
- [Set Up Pyright Type Checking In GitHub](python/set-up-pyright-type-checking-in-github.md)
|
|
||||||
- [Skip Specific Pytest Test Cases](python/skip-specific-pytest-test-cases.md)
|
|
||||||
- [Sort A List Of Dataclass Instances](python/sort-a-list-of-dataclass-instances.md)
|
|
||||||
- [Sort Normalized Version Of Data](python/sort-normalized-version-of-data.md)
|
|
||||||
- [Start The Debugger When A Test Errors](python/start-the-debugger-when-a-test-errors.md)
|
|
||||||
- [Store And Access Immutable Data In A Tuple](python/store-and-access-immutable-data-in-a-tuple.md)
|
- [Store And Access Immutable Data In A Tuple](python/store-and-access-immutable-data-in-a-tuple.md)
|
||||||
- [Test A Function With Pytest](python/test-a-function-with-pytest.md)
|
- [Test A Function With Pytest](python/test-a-function-with-pytest.md)
|
||||||
- [Turn Method Into Cached Property On Class Instance](python/turn-method-into-cached-property-on-class-instance.md)
|
|
||||||
- [Use pipx To Install End User Apps](python/use-pipx-to-install-end-user-apps.md)
|
- [Use pipx To Install End User Apps](python/use-pipx-to-install-end-user-apps.md)
|
||||||
- [Use `__post_init__` For `dataclass` Validations](python/use-post-init-for-dataclass-validations.md)
|
|
||||||
- [Use Verbose Flag To Get More Diff](python/use-verbose-flag-to-get-more-diff.md)
|
|
||||||
- [Validate Click Option With Callback](python/validate-click-option-with-callback.md)
|
|
||||||
|
|
||||||
### Rails
|
### Rails
|
||||||
|
|
||||||
@@ -1138,18 +1008,14 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Cast Common Boolean-Like Values To Booleans](rails/cast-common-boolean-like-values-to-booleans.md)
|
- [Cast Common Boolean-Like Values To Booleans](rails/cast-common-boolean-like-values-to-booleans.md)
|
||||||
- [Change The Nullability Of A Column](rails/change-the-nullability-of-a-column.md)
|
- [Change The Nullability Of A Column](rails/change-the-nullability-of-a-column.md)
|
||||||
- [Change The Time Zone Offset Of A DateTime Object](rails/change-the-time-zone-offset-of-a-datetime-object.md)
|
- [Change The Time Zone Offset Of A DateTime Object](rails/change-the-time-zone-offset-of-a-datetime-object.md)
|
||||||
- [Check How Database Is Configured](rails/check-how-database-is-configured.md)
|
|
||||||
- [Check If ActiveRecord Update Fails](rails/check-if-activerecord-update-fails.md)
|
- [Check If ActiveRecord Update Fails](rails/check-if-activerecord-update-fails.md)
|
||||||
- [Check If Any Records Have A Null Value](rails/check-if-any-records-have-a-null-value.md)
|
- [Check If Any Records Have A Null Value](rails/check-if-any-records-have-a-null-value.md)
|
||||||
- [Check Specific Attributes On ActiveRecord Array](rails/check-specific-attributes-on-activerecord-array.md)
|
- [Check Specific Attributes On ActiveRecord Array](rails/check-specific-attributes-on-activerecord-array.md)
|
||||||
- [Check The Current Named Log Level](rails/check-the-current-named-log-level.md)
|
|
||||||
- [Clean Up Memory Hungry Rails Console Processes](rails/clean-up-memory-hungry-rails-console-processes.md)
|
|
||||||
- [Code Statistics For An Application](rails/code-statistics-for-an-application.md)
|
- [Code Statistics For An Application](rails/code-statistics-for-an-application.md)
|
||||||
- [Columns With Default Values Are Nil On Create](rails/columns-with-default-values-are-nil-on-create.md)
|
- [Columns With Default Values Are Nil On Create](rails/columns-with-default-values-are-nil-on-create.md)
|
||||||
- [Comparing DateTimes Down To Second Precision](rails/comparing-datetimes-down-to-second-precision.md)
|
- [Comparing DateTimes Down To Second Precision](rails/comparing-datetimes-down-to-second-precision.md)
|
||||||
- [Conditional Class Selectors in Haml](rails/conditional-class-selectors-in-haml.md)
|
- [Conditional Class Selectors in Haml](rails/conditional-class-selectors-in-haml.md)
|
||||||
- [Convert A Symbol To A Constant](rails/convert-a-symbol-to-a-constant.md)
|
- [Convert A Symbol To A Constant](rails/convert-a-symbol-to-a-constant.md)
|
||||||
- [Convert JSON Field To Hash With Indifferent Access](rails/convert-json-field-to-hash-with-indifferent-access.md)
|
|
||||||
- [Count The Number Of Records By Attribute](rails/count-the-number-of-records-by-attribute.md)
|
- [Count The Number Of Records By Attribute](rails/count-the-number-of-records-by-attribute.md)
|
||||||
- [Create A Custom Named References Column](rails/create-a-custom-named-references-column.md)
|
- [Create A Custom Named References Column](rails/create-a-custom-named-references-column.md)
|
||||||
- [Create A Join Table With The Migration DSL](rails/create-a-join-table-with-the-migration-dsl.md)
|
- [Create A Join Table With The Migration DSL](rails/create-a-join-table-with-the-migration-dsl.md)
|
||||||
@@ -1157,9 +1023,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Creating Records of Has_One Associations](rails/creating-records-of-has-one-associations.md)
|
- [Creating Records of Has_One Associations](rails/creating-records-of-has-one-associations.md)
|
||||||
- [Custom Validation Message](rails/custom-validation-message.md)
|
- [Custom Validation Message](rails/custom-validation-message.md)
|
||||||
- [Customize Paths And Helpers For Devise Routes](rails/customize-paths-and-helpers-for-devise-routes.md)
|
- [Customize Paths And Helpers For Devise Routes](rails/customize-paths-and-helpers-for-devise-routes.md)
|
||||||
- [Customize Template For New Schema Migration](rails/customize-template-for-new-schema-migration.md)
|
|
||||||
- [Customize The Path Of A Resource Route](rails/customize-the-path-of-a-resource-route.md)
|
- [Customize The Path Of A Resource Route](rails/customize-the-path-of-a-resource-route.md)
|
||||||
- [Define Conditional Routing Logic In Routes File](rails/define-conditional-routing-logic-in-routes-file.md)
|
|
||||||
- [Define The Root Path For The App](rails/define-the-root-path-for-the-app.md)
|
- [Define The Root Path For The App](rails/define-the-root-path-for-the-app.md)
|
||||||
- [Delete Paranoid Records](rails/delete-paranoid-records.md)
|
- [Delete Paranoid Records](rails/delete-paranoid-records.md)
|
||||||
- [Demodulize A Class Name](rails/demodulize-a-class-name.md)
|
- [Demodulize A Class Name](rails/demodulize-a-class-name.md)
|
||||||
@@ -1171,13 +1035,10 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Ensure A Rake Task Cannot Write Data](rails/ensure-a-rake-task-cannot-write-data.md)
|
- [Ensure A Rake Task Cannot Write Data](rails/ensure-a-rake-task-cannot-write-data.md)
|
||||||
- [Ensure Migrations Use The Latest Schema](rails/ensure-migrations-use-the-latest-schema.md)
|
- [Ensure Migrations Use The Latest Schema](rails/ensure-migrations-use-the-latest-schema.md)
|
||||||
- [Ensure Record Saved With after_commit Callback](rails/ensure-record-saved-with-after-commit-callback.md)
|
- [Ensure Record Saved With after_commit Callback](rails/ensure-record-saved-with-after-commit-callback.md)
|
||||||
- [Filter ActiveModel Validation Errors](rails/filter-active-model-validation-errors.md)
|
|
||||||
- [Filter ActiveStorage Blobs To Only Images](rails/filter-active-storage-blobs-to-only-images.md)
|
- [Filter ActiveStorage Blobs To Only Images](rails/filter-active-storage-blobs-to-only-images.md)
|
||||||
- [Find Or Create A Record With FactoryBot](rails/find-or-create-a-record-with-factory-bot.md)
|
- [Find Or Create A Record With FactoryBot](rails/find-or-create-a-record-with-factory-bot.md)
|
||||||
- [Find Records With Multiple Associated Records](rails/find-records-with-multiple-associated-records.md)
|
- [Find Records With Multiple Associated Records](rails/find-records-with-multiple-associated-records.md)
|
||||||
- [Force All Users To Sign Out](rails/force-all-users-to-sign-out.md)
|
- [Force All Users To Sign Out](rails/force-all-users-to-sign-out.md)
|
||||||
- [Format DateTime With Builtin Formats](rails/format-datetime-with-builtin-formats.md)
|
|
||||||
- [Format Specific html.erb Template Files](rails/format-specific-html-erb-template-files.md)
|
|
||||||
- [Generate A Model](rails/generate-a-model.md)
|
- [Generate A Model](rails/generate-a-model.md)
|
||||||
- [Generate A Rails App From The Main Branch](rails/generate-a-rails-app-from-the-main-branch.md)
|
- [Generate A Rails App From The Main Branch](rails/generate-a-rails-app-from-the-main-branch.md)
|
||||||
- [Generating And Executing SQL](rails/generating-and-executing-sql.md)
|
- [Generating And Executing SQL](rails/generating-and-executing-sql.md)
|
||||||
@@ -1190,7 +1051,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Get The Column Names For A Model](rails/get-the-column-names-for-a-model.md)
|
- [Get The Column Names For A Model](rails/get-the-column-names-for-a-model.md)
|
||||||
- [Get The Current Time](rails/get-the-current-time.md)
|
- [Get The Current Time](rails/get-the-current-time.md)
|
||||||
- [Grab A Random Record From The Database](rails/grab-a-random-record-from-the-database.md)
|
- [Grab A Random Record From The Database](rails/grab-a-random-record-from-the-database.md)
|
||||||
- [Halt ActionMailer Delivery With Callback](rails/halt-action-mailer-delivery-with-callback.md)
|
|
||||||
- [Handle Named Arguments In A Rake Task](rails/handle-named-arguments-in-a-rake-task.md)
|
- [Handle Named Arguments In A Rake Task](rails/handle-named-arguments-in-a-rake-task.md)
|
||||||
- [Hash Slicing](rails/hash-slicing.md)
|
- [Hash Slicing](rails/hash-slicing.md)
|
||||||
- [Ignore Poltergeist JavaScript Errors](rails/ignore-poltergeist-javascript-errors.md)
|
- [Ignore Poltergeist JavaScript Errors](rails/ignore-poltergeist-javascript-errors.md)
|
||||||
@@ -1208,7 +1068,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Make A String Attribute Easy To Inquire About](rails/make-a-string-attribute-easy-to-inquire-about.md)
|
- [Make A String Attribute Easy To Inquire About](rails/make-a-string-attribute-easy-to-inquire-about.md)
|
||||||
- [Make ActionMailer Synchronous In Test](rails/make-action-mailer-synchronous-in-test.md)
|
- [Make ActionMailer Synchronous In Test](rails/make-action-mailer-synchronous-in-test.md)
|
||||||
- [Make Remove Column Migration Reversible](rails/make-remove-column-migration-reversible.md)
|
- [Make Remove Column Migration Reversible](rails/make-remove-column-migration-reversible.md)
|
||||||
- [Manage Timestamps With Upsert](rails/manage-timestamps-with-upsert.md)
|
|
||||||
- [Manually Run A Migration From Rails Console](rails/manually-run-a-migration-from-rails-console.md)
|
- [Manually Run A Migration From Rails Console](rails/manually-run-a-migration-from-rails-console.md)
|
||||||
- [Mark For Destruction](rails/mark-for-destruction.md)
|
- [Mark For Destruction](rails/mark-for-destruction.md)
|
||||||
- [Mask An ActiveRecord Attribute](rails/mask-an-activerecord-attribute.md)
|
- [Mask An ActiveRecord Attribute](rails/mask-an-activerecord-attribute.md)
|
||||||
@@ -1217,7 +1076,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Mock Rails Environment With An Inquiry Instance](rails/mock-rails-environment-with-an-inquiry-instance.md)
|
- [Mock Rails Environment With An Inquiry Instance](rails/mock-rails-environment-with-an-inquiry-instance.md)
|
||||||
- [Order Matters For `rescue_from` Blocks](rails/order-matters-for-rescue-from-blocks.md)
|
- [Order Matters For `rescue_from` Blocks](rails/order-matters-for-rescue-from-blocks.md)
|
||||||
- [Override Text Displayed By Form Label](rails/override-text-displayed-by-form-label.md)
|
- [Override Text Displayed By Form Label](rails/override-text-displayed-by-form-label.md)
|
||||||
- [Parameterize A String With Underscores](rails/parameterize-a-string-with-underscores.md)
|
|
||||||
- [Params Includes Submission Button Info](rails/params-includes-submission-button-info.md)
|
- [Params Includes Submission Button Info](rails/params-includes-submission-button-info.md)
|
||||||
- [Params Is A Hash With Indifferent Access](rails/params-is-a-hash-with-indifferent-access.md)
|
- [Params Is A Hash With Indifferent Access](rails/params-is-a-hash-with-indifferent-access.md)
|
||||||
- [Parse Query Params From A URL](rails/parse-query-params-from-a-url.md)
|
- [Parse Query Params From A URL](rails/parse-query-params-from-a-url.md)
|
||||||
@@ -1226,9 +1084,7 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Polymorphic Path Helpers](rails/polymorphic-path-helpers.md)
|
- [Polymorphic Path Helpers](rails/polymorphic-path-helpers.md)
|
||||||
- [Prefer select_all Over execute For Read Queries](rails/prefer-select-all-over-execute-for-read-queries.md)
|
- [Prefer select_all Over execute For Read Queries](rails/prefer-select-all-over-execute-for-read-queries.md)
|
||||||
- [Pretend Generations](rails/pretend-generations.md)
|
- [Pretend Generations](rails/pretend-generations.md)
|
||||||
- [Prevent Mailer Previews From Cluttering Database](rails/prevent-mailer-previews-from-cluttering-database.md)
|
|
||||||
- [Prevent Writes With A Sandboxed Rails Console](rails/prevent-writes-with-a-sandboxed-rails-console.md)
|
- [Prevent Writes With A Sandboxed Rails Console](rails/prevent-writes-with-a-sandboxed-rails-console.md)
|
||||||
- [Provide Fake Form Helper To Controllers](rails/provide-fake-form-helper-to-controllers.md)
|
|
||||||
- [Query A Single Value From The Database](rails/query-a-single-value-from-the-database.md)
|
- [Query A Single Value From The Database](rails/query-a-single-value-from-the-database.md)
|
||||||
- [Read In Environment-Specific Config Values](rails/read-in-environment-specific-config-values.md)
|
- [Read In Environment-Specific Config Values](rails/read-in-environment-specific-config-values.md)
|
||||||
- [Read-Only Models](rails/read-only-models.md)
|
- [Read-Only Models](rails/read-only-models.md)
|
||||||
@@ -1248,12 +1104,9 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Rounding Numbers With Precision](rails/rounding-numbers-with-precision.md)
|
- [Rounding Numbers With Precision](rails/rounding-numbers-with-precision.md)
|
||||||
- [Run A Rake Task Programmatically](rails/run-a-rake-task-programmatically.md)
|
- [Run A Rake Task Programmatically](rails/run-a-rake-task-programmatically.md)
|
||||||
- [Run Commands With Specific Rails Version](rails/run-commands-with-specific-rails-version.md)
|
- [Run Commands With Specific Rails Version](rails/run-commands-with-specific-rails-version.md)
|
||||||
- [Run Dev Processes With Overmind Instead Of Foreman](rails/run-dev-processes-with-overmind-instead-of-foreman.md)
|
|
||||||
- [Run Rails Console With Remote Dokku App](rails/run-rails-console-with-remote-dokku-app.md)
|
|
||||||
- [Run Some Code Whenever Rails Console Starts](rails/run-some-code-whenever-rails-console-starts.md)
|
- [Run Some Code Whenever Rails Console Starts](rails/run-some-code-whenever-rails-console-starts.md)
|
||||||
- [Scaffold Auth Functionality With Rails 8 Generator](rails/scaffold-auth-functionality-with-rails-8-generator.md)
|
- [Scaffold Auth Functionality With Rails 8 Generator](rails/scaffold-auth-functionality-with-rails-8-generator.md)
|
||||||
- [Schedule Sidekiq Jobs Out Into The Future](rails/schedule-sidekiq-jobs-out-into-the-future.md)
|
- [Schedule Sidekiq Jobs Out Into The Future](rails/schedule-sidekiq-jobs-out-into-the-future.md)
|
||||||
- [Scope Records To A Lower Or Upper Bound](rails/scope-records-to-a-lower-or-upper-bound.md)
|
|
||||||
- [Secure Passwords With Rails And Bcrypt](rails/secure-passwords-with-rails-and-bcrypt.md)
|
- [Secure Passwords With Rails And Bcrypt](rails/secure-passwords-with-rails-and-bcrypt.md)
|
||||||
- [Select A Select By Selector](rails/select-a-select-by-selector.md)
|
- [Select A Select By Selector](rails/select-a-select-by-selector.md)
|
||||||
- [Select A Specific Rails Version To Install](rails/select-a-specific-rails-version-to-install.md)
|
- [Select A Specific Rails Version To Install](rails/select-a-specific-rails-version-to-install.md)
|
||||||
@@ -1282,7 +1135,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Update Column Versus Update Attribute](rails/update-column-versus-update-attribute.md)
|
- [Update Column Versus Update Attribute](rails/update-column-versus-update-attribute.md)
|
||||||
- [Upgrading Your Manifest For Sprocket's 4](rails/upgrading-your-manifest-for-sprockets-4.md)
|
- [Upgrading Your Manifest For Sprocket's 4](rails/upgrading-your-manifest-for-sprockets-4.md)
|
||||||
- [Use IRB And Ruby Flags With Rails Console](rails/use-irb-and-ruby-flags-with-rails-console.md)
|
- [Use IRB And Ruby Flags With Rails Console](rails/use-irb-and-ruby-flags-with-rails-console.md)
|
||||||
- [Use .ruby Extension For Template File](rails/use-ruby-extension-for-template-file.md)
|
|
||||||
- [Useful ActiveSupport Constants For Durations](rails/useful-active-support-constants-for-durations.md)
|
- [Useful ActiveSupport Constants For Durations](rails/useful-active-support-constants-for-durations.md)
|
||||||
- [Validate Column Data With Check Constraints](rails/validate-column-data-with-check-constraints.md)
|
- [Validate Column Data With Check Constraints](rails/validate-column-data-with-check-constraints.md)
|
||||||
- [Verify And Read A Signed Cookie Value](rails/verify-and-read-a-signed-cookie-value.md)
|
- [Verify And Read A Signed Cookie Value](rails/verify-and-read-a-signed-cookie-value.md)
|
||||||
@@ -1422,9 +1274,8 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Add Progress Reporting To Long-Running Script](ruby/add-progress-reporting-to-long-running-script.md)
|
- [Add Progress Reporting To Long-Running Script](ruby/add-progress-reporting-to-long-running-script.md)
|
||||||
- [Are They All True?](ruby/are-they-all-true.md)
|
- [Are They All True?](ruby/are-they-all-true.md)
|
||||||
- [Assert About An Object's Attributes With RSpec](ruby/assert-about-an-objects-attributes-with-rspec.md)
|
- [Assert About An Object's Attributes With RSpec](ruby/assert-about-an-objects-attributes-with-rspec.md)
|
||||||
- [Assoc For Hashes](ruby/assoc-for-hashes.md)
|
|
||||||
- [Audit Your Ruby Project For Any CVEs](ruby/audit-your-ruby-project-for-any-cves.md)
|
- [Audit Your Ruby Project For Any CVEs](ruby/audit-your-ruby-project-for-any-cves.md)
|
||||||
- [Avoid Double Negation With Minitest Refute](ruby/avoid-double-negation-with-minitest-refute.md)
|
- [Assoc For Hashes](ruby/assoc-for-hashes.md)
|
||||||
- [Block Comments](ruby/block-comments.md)
|
- [Block Comments](ruby/block-comments.md)
|
||||||
- [Block Syntaxes Have Different Precedence](ruby/block-syntaxes-have-different-precedence.md)
|
- [Block Syntaxes Have Different Precedence](ruby/block-syntaxes-have-different-precedence.md)
|
||||||
- [Build HTTP And HTTPS URLs](ruby/build-http-and-https-urls.md)
|
- [Build HTTP And HTTPS URLs](ruby/build-http-and-https-urls.md)
|
||||||
@@ -1444,15 +1295,11 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Create a CSV::Table Object](ruby/create-a-csv-table-object.md)
|
- [Create a CSV::Table Object](ruby/create-a-csv-table-object.md)
|
||||||
- [Create A Hash From An Array Of Arrays](ruby/create-a-hash-from-an-array-of-arrays.md)
|
- [Create A Hash From An Array Of Arrays](ruby/create-a-hash-from-an-array-of-arrays.md)
|
||||||
- [Create Listing Of All Middleman Pages](ruby/create-listing-of-all-middleman-pages.md)
|
- [Create Listing Of All Middleman Pages](ruby/create-listing-of-all-middleman-pages.md)
|
||||||
- [Create Mock Class That Can Be Overridden](ruby/create-mock-class-that-can-be-overridden.md)
|
|
||||||
- [Create A Module Of Utility Functions](ruby/create-a-module-of-utility-functions.md)
|
|
||||||
- [Create Named Structs With Struct.new](ruby/create-named-structs-with-struct-new.md)
|
- [Create Named Structs With Struct.new](ruby/create-named-structs-with-struct-new.md)
|
||||||
- [Create Thumbnail Image For A PDF](ruby/create-thumbnail-image-for-a-pdf.md)
|
- [Create Thumbnail Image For A PDF](ruby/create-thumbnail-image-for-a-pdf.md)
|
||||||
- [Decompose Unicode Character With Diacritic Mark](ruby/decompose-unicode-character-with-diacritic-mark.md)
|
|
||||||
- [Defaulting To Frozen String Literals](ruby/defaulting-to-frozen-string-literals.md)
|
- [Defaulting To Frozen String Literals](ruby/defaulting-to-frozen-string-literals.md)
|
||||||
- [Define A Custom RSpec Matcher](ruby/define-a-custom-rspec-matcher.md)
|
- [Define A Custom RSpec Matcher](ruby/define-a-custom-rspec-matcher.md)
|
||||||
- [Define A Method On A Struct](ruby/define-a-method-on-a-struct.md)
|
- [Define A Method On A Struct](ruby/define-a-method-on-a-struct.md)
|
||||||
- [Define A Set Of Class Methods](ruby/define-a-set-of-class-methods.md)
|
|
||||||
- [Define Multiline Strings With Heredocs](ruby/define-multiline-strings-with-heredocs.md)
|
- [Define Multiline Strings With Heredocs](ruby/define-multiline-strings-with-heredocs.md)
|
||||||
- [Destructure The First Item From An Array](ruby/destructure-the-first-item-from-an-array.md)
|
- [Destructure The First Item From An Array](ruby/destructure-the-first-item-from-an-array.md)
|
||||||
- [Destructuring Arrays In Blocks](ruby/destructuring-arrays-in-blocks.md)
|
- [Destructuring Arrays In Blocks](ruby/destructuring-arrays-in-blocks.md)
|
||||||
@@ -1473,7 +1320,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [FactoryGirl Sequences](ruby/factory-girl-sequences.md)
|
- [FactoryGirl Sequences](ruby/factory-girl-sequences.md)
|
||||||
- [Fail](ruby/fail.md)
|
- [Fail](ruby/fail.md)
|
||||||
- [Fetch Warns About Superseding Block Argument](ruby/fetch-warns-about-superseding-block-argument.md)
|
- [Fetch Warns About Superseding Block Argument](ruby/fetch-warns-about-superseding-block-argument.md)
|
||||||
- [Filter By Type](ruby/filter-by-type.md)
|
|
||||||
- [Find The Min And Max With A Single Call](ruby/find-the-min-and-max-with-a-single-call.md)
|
- [Find The Min And Max With A Single Call](ruby/find-the-min-and-max-with-a-single-call.md)
|
||||||
- [Finding The Source of Ruby Methods](ruby/finding-the-source-of-ruby-methods.md)
|
- [Finding The Source of Ruby Methods](ruby/finding-the-source-of-ruby-methods.md)
|
||||||
- [Format A Hash Into A String Template](ruby/format-a-hash-into-a-string-template.md)
|
- [Format A Hash Into A String Template](ruby/format-a-hash-into-a-string-template.md)
|
||||||
@@ -1482,8 +1328,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Generate A Signed JWT Token](ruby/generate-a-signed-jwt-token.md)
|
- [Generate A Signed JWT Token](ruby/generate-a-signed-jwt-token.md)
|
||||||
- [Generate Ruby Version And Gemset Files With RVM](ruby/generate-ruby-version-and-gemset-files-with-rvm.md)
|
- [Generate Ruby Version And Gemset Files With RVM](ruby/generate-ruby-version-and-gemset-files-with-rvm.md)
|
||||||
- [Get Info About Your RubyGems Environment](ruby/get-info-about-your-ruby-gems-environment.md)
|
- [Get Info About Your RubyGems Environment](ruby/get-info-about-your-ruby-gems-environment.md)
|
||||||
- [Get Specific Values From Arrays And Hashes](ruby/get-specific-values-from-hashes-and-arrays.md)
|
|
||||||
- [Get The Names Of The Month](ruby/get-the-names-of-the-month.md)
|
|
||||||
- [Get The Output Of Running A System Program](ruby/get-the-output-of-running-a-system-program.md)
|
- [Get The Output Of Running A System Program](ruby/get-the-output-of-running-a-system-program.md)
|
||||||
- [Get UTC Offset For Different Time Zones](ruby/get-utc-offset-for-different-time-zones.md)
|
- [Get UTC Offset For Different Time Zones](ruby/get-utc-offset-for-different-time-zones.md)
|
||||||
- [Identify Outdated Gems](ruby/identify-outdated-gems.md)
|
- [Identify Outdated Gems](ruby/identify-outdated-gems.md)
|
||||||
@@ -1491,20 +1335,15 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Iterate With An Offset Index](ruby/iterate-with-an-offset-index.md)
|
- [Iterate With An Offset Index](ruby/iterate-with-an-offset-index.md)
|
||||||
- [Include Extra Context In A Honeybadger Notify](ruby/include-extra-context-in-a-honeybadger-notify.md)
|
- [Include Extra Context In A Honeybadger Notify](ruby/include-extra-context-in-a-honeybadger-notify.md)
|
||||||
- [Ins And Outs Of Pry](ruby/ins-and-outs-of-pry.md)
|
- [Ins And Outs Of Pry](ruby/ins-and-outs-of-pry.md)
|
||||||
- [Install And Require Gems Inline Without Gemfile](ruby/install-and-require-gems-inline-without-gemfile.md)
|
|
||||||
- [Install Latest Version Of Ruby With asdf](ruby/install-latest-version-of-ruby-with-asdf.md)
|
- [Install Latest Version Of Ruby With asdf](ruby/install-latest-version-of-ruby-with-asdf.md)
|
||||||
- [Invoking Rake Tasks Multiple Times](ruby/invoking-rake-tasks-multiple-times.md)
|
- [Invoking Rake Tasks Multiple Times](ruby/invoking-rake-tasks-multiple-times.md)
|
||||||
- [IRB Has Built-In Benchmarking With Ruby 3](ruby/irb-has-built-in-benchmarking-with-ruby-3.md)
|
- [IRB Has Built-In Benchmarking With Ruby 3](ruby/irb-has-built-in-benchmarking-with-ruby-3.md)
|
||||||
- [Join URI Path Parts](ruby/join-uri-path-parts.md)
|
|
||||||
- [Jump Out Of A Nested Context With Throw/Catch](ruby/jump-out-of-a-nested-context-with-throw-catch.md)
|
- [Jump Out Of A Nested Context With Throw/Catch](ruby/jump-out-of-a-nested-context-with-throw-catch.md)
|
||||||
- [Last Raised Exception In The Call Stack](ruby/last-raised-exception-in-the-call-stack.md)
|
- [Last Raised Exception In The Call Stack](ruby/last-raised-exception-in-the-call-stack.md)
|
||||||
- [Limit Split](ruby/limit-split.md)
|
- [Limit Split](ruby/limit-split.md)
|
||||||
- [List The Running Ruby Version](ruby/list-the-running-ruby-version.md)
|
- [List The Running Ruby Version](ruby/list-the-running-ruby-version.md)
|
||||||
- [Listing Local Variables](ruby/listing-local-variables.md)
|
- [Listing Local Variables](ruby/listing-local-variables.md)
|
||||||
- [Load A Module And Execute A Statement](ruby/load-a-module-and-execute-a-statement.md)
|
|
||||||
- [Make A Long String Of Text Readable](ruby/make-a-long-string-of-text-readable.md)
|
|
||||||
- [Make An Executable Ruby Script](ruby/make-an-executable-ruby-script.md)
|
- [Make An Executable Ruby Script](ruby/make-an-executable-ruby-script.md)
|
||||||
- [Make Structs Easier To Use With Keyword Initialization](ruby/make-structs-easier-to-use-with-keyword-initialization.md)
|
|
||||||
- [Map With Index Over An Array](ruby/map-with-index-over-an-array.md)
|
- [Map With Index Over An Array](ruby/map-with-index-over-an-array.md)
|
||||||
- [Mock Method Chain Calls With RSpec](ruby/mock-method-chain-calls-with-rspec.md)
|
- [Mock Method Chain Calls With RSpec](ruby/mock-method-chain-calls-with-rspec.md)
|
||||||
- [Mocking Requests With Partial URIs Using Regex](ruby/mocking-requests-with-partial-uris-using-regex.md)
|
- [Mocking Requests With Partial URIs Using Regex](ruby/mocking-requests-with-partial-uris-using-regex.md)
|
||||||
@@ -1531,8 +1370,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Rake Only Lists Tasks With Descriptions](ruby/rake-only-lists-tasks-with-descriptions.md)
|
- [Rake Only Lists Tasks With Descriptions](ruby/rake-only-lists-tasks-with-descriptions.md)
|
||||||
- [Read The First Line From A File](ruby/read-the-first-line-from-a-file.md)
|
- [Read The First Line From A File](ruby/read-the-first-line-from-a-file.md)
|
||||||
- [Refer To Implicit Block Argument With It](ruby/refer-to-implicit-block-argument-with-it.md)
|
- [Refer To Implicit Block Argument With It](ruby/refer-to-implicit-block-argument-with-it.md)
|
||||||
- [Reference Hash Key With Safe Navigation](ruby/reference-hash-key-with-safe-navigation.md)
|
|
||||||
- [Regenerate Lock File With Newer Bundler](ruby/regenerate-lock-file-with-newer-bundler.md)
|
|
||||||
- [Rendering ERB](ruby/rendering-erb.md)
|
- [Rendering ERB](ruby/rendering-erb.md)
|
||||||
- [Replace The Current Process With An External Command](ruby/replace-the-current-process-with-an-external-command.md)
|
- [Replace The Current Process With An External Command](ruby/replace-the-current-process-with-an-external-command.md)
|
||||||
- [Require Entire Gemfile In Pry Session](ruby/require-entire-gemfile-in-pry-session.md)
|
- [Require Entire Gemfile In Pry Session](ruby/require-entire-gemfile-in-pry-session.md)
|
||||||
@@ -1547,7 +1384,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Scripting With RVM](ruby/scripting-with-rvm.md)
|
- [Scripting With RVM](ruby/scripting-with-rvm.md)
|
||||||
- [Scroll To Top Of Page With Capybara](ruby/scroll-to-top-of-page-with-capybara.md)
|
- [Scroll To Top Of Page With Capybara](ruby/scroll-to-top-of-page-with-capybara.md)
|
||||||
- [Search For Gem Versions Available To Install](ruby/search-for-gem-versions-available-to-install.md)
|
- [Search For Gem Versions Available To Install](ruby/search-for-gem-versions-available-to-install.md)
|
||||||
- [Set Default Tasks For Rake To Run](ruby/set-default-tasks-for-rake-to-run.md)
|
|
||||||
- [Set RVM Default Ruby](ruby/set-rvm-default-ruby.md)
|
- [Set RVM Default Ruby](ruby/set-rvm-default-ruby.md)
|
||||||
- [Shift The Month On A Date Object](ruby/shift-the-month-on-a-date-object.md)
|
- [Shift The Month On A Date Object](ruby/shift-the-month-on-a-date-object.md)
|
||||||
- [Show Public Methods With Pry](ruby/show-public-methods-with-pry.md)
|
- [Show Public Methods With Pry](ruby/show-public-methods-with-pry.md)
|
||||||
@@ -1556,7 +1392,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Single And Double Quoted String Notation](ruby/single-and-double-quoted-string-notation.md)
|
- [Single And Double Quoted String Notation](ruby/single-and-double-quoted-string-notation.md)
|
||||||
- [Skip Specific CVEs When Auditing Your Bundle](ruby/skip-specific-cves-when-auditing-your-bundle.md)
|
- [Skip Specific CVEs When Auditing Your Bundle](ruby/skip-specific-cves-when-auditing-your-bundle.md)
|
||||||
- [Skip The Front Of An Array With Drop](ruby/skip-the-front-of-an-array-with-drop.md)
|
- [Skip The Front Of An Array With Drop](ruby/skip-the-front-of-an-array-with-drop.md)
|
||||||
- [Specify Default For Data Definition](ruby/specify-default-for-data-definition.md)
|
|
||||||
- [Specify Dependencies For A Rake Task](ruby/specify-dependencies-for-a-rake-task.md)
|
- [Specify Dependencies For A Rake Task](ruby/specify-dependencies-for-a-rake-task.md)
|
||||||
- [Specify How Random Array#sample Is](ruby/specify-how-random-array-sample-is.md)
|
- [Specify How Random Array#sample Is](ruby/specify-how-random-array-sample-is.md)
|
||||||
- [Split A Float Into Its Integer And Decimal](ruby/split-a-float-into-its-integer-and-decimal.md)
|
- [Split A Float Into Its Integer And Decimal](ruby/split-a-float-into-its-integer-and-decimal.md)
|
||||||
@@ -1576,7 +1411,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Update The Gemfile Bundled With Version](ruby/update-the-gemfile-bundled-with-version.md)
|
- [Update The Gemfile Bundled With Version](ruby/update-the-gemfile-bundled-with-version.md)
|
||||||
- [Use A Case Statement As A Cond Statement](ruby/use-a-case-statement-as-a-cond-statement.md)
|
- [Use A Case Statement As A Cond Statement](ruby/use-a-case-statement-as-a-cond-statement.md)
|
||||||
- [Use dotenv In A Non-Rails Project](ruby/use-dotenv-in-a-non-rails-project.md)
|
- [Use dotenv In A Non-Rails Project](ruby/use-dotenv-in-a-non-rails-project.md)
|
||||||
- [Use Rescue As Part Of Inline Statement](ruby/use-rescue-as-part-of-inline-statement.md)
|
|
||||||
- [Use Tap For Better Test Data Setup](ruby/use-tap-for-better-test-data-setup.md)
|
- [Use Tap For Better Test Data Setup](ruby/use-tap-for-better-test-data-setup.md)
|
||||||
- [Using BCrypt To Create And Check Hashed Passwords](ruby/using-bcrypt-to-create-and-check-hashed-passwords.md)
|
- [Using BCrypt To Create And Check Hashed Passwords](ruby/using-bcrypt-to-create-and-check-hashed-passwords.md)
|
||||||
- [What To Do When You Don't Rescue](ruby/what-to-do-when-you-dont-rescue.md)
|
- [What To Do When You Don't Rescue](ruby/what-to-do-when-you-dont-rescue.md)
|
||||||
@@ -1594,7 +1428,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [OSX sed Does Regex A Bit Different](sed/osx-sed-does-regex-a-bit-different.md)
|
- [OSX sed Does Regex A Bit Different](sed/osx-sed-does-regex-a-bit-different.md)
|
||||||
- [Output Only Lines Involved In A Substitution](sed/output-only-lines-involved-in-a-substitution.md)
|
- [Output Only Lines Involved In A Substitution](sed/output-only-lines-involved-in-a-substitution.md)
|
||||||
- [Reference A Capture In The Regex](sed/reference-a-capture-in-the-regex.md)
|
- [Reference A Capture In The Regex](sed/reference-a-capture-in-the-regex.md)
|
||||||
- [Reference The Full Match In The Replacement](sed/reference-the-full-match-in-the-replacement.md)
|
|
||||||
- [Use An Alternative Delimiter In A Substitution](sed/use-an-alternative-delimiter-in-a-substitution.md)
|
- [Use An Alternative Delimiter In A Substitution](sed/use-an-alternative-delimiter-in-a-substitution.md)
|
||||||
|
|
||||||
### Shell
|
### Shell
|
||||||
@@ -1615,20 +1448,12 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
|
|
||||||
- [Apply Tailwind Classes To Existing CSS Class](tailwind/apply-tailwind-classes-to-existing-css-class.md)
|
- [Apply Tailwind Classes To Existing CSS Class](tailwind/apply-tailwind-classes-to-existing-css-class.md)
|
||||||
- [Base Styles For Text Link](tailwind/base-styles-for-text-link.md)
|
- [Base Styles For Text Link](tailwind/base-styles-for-text-link.md)
|
||||||
- [Disable And Enable A Button](tailwind/disable-and-enable-a-button.md)
|
|
||||||
- [Specify Paths For Purging Unused CSS](tailwind/specify-paths-for-purging-unused-css.md)
|
- [Specify Paths For Purging Unused CSS](tailwind/specify-paths-for-purging-unused-css.md)
|
||||||
- [Use Tailwind Typography Prose In Dark Mode](tailwind/use-tailwind-typography-prose-in-dark-mode.md)
|
- [Use Tailwind Typography Prose In Dark Mode](tailwind/use-tailwind-typography-prose-in-dark-mode.md)
|
||||||
|
|
||||||
### Taskfile
|
|
||||||
|
|
||||||
- [Add Default Task To List All Tasks](taskfile/add-default-task-to-list-all-tasks.md)
|
|
||||||
- [Create Interactive Picker For Set Of Subtasks](taskfile/create-interactive-picker-for-set-of-subtasks.md)
|
|
||||||
- [Run A Task If It Meets Criteria](taskfile/run-a-task-if-it-meets-criteria.md)
|
|
||||||
|
|
||||||
### tmux
|
### tmux
|
||||||
|
|
||||||
- [Access Past Copy Buffer History](tmux/access-past-copy-buffer-history.md)
|
- [Access Past Copy Buffer History](tmux/access-past-copy-buffer-history.md)
|
||||||
- [Add Bindings To Split Panes To Current Directory](tmux/add-bindings-to-split-panes-to-current-directory.md)
|
|
||||||
- [Adjusting Window Pane Size](tmux/adjusting-window-pane-size.md)
|
- [Adjusting Window Pane Size](tmux/adjusting-window-pane-size.md)
|
||||||
- [Break Current Pane Out To Separate Window](tmux/break-current-pane-out-to-separate-window.md)
|
- [Break Current Pane Out To Separate Window](tmux/break-current-pane-out-to-separate-window.md)
|
||||||
- [Change Base Directory Of Existing Session](tmux/change-base-directory-of-existing-session.md)
|
- [Change Base Directory Of Existing Session](tmux/change-base-directory-of-existing-session.md)
|
||||||
@@ -1646,7 +1471,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Kill Other Connections To A Session](tmux/kill-other-connections-to-a-session.md)
|
- [Kill Other Connections To A Session](tmux/kill-other-connections-to-a-session.md)
|
||||||
- [Kill The Current Session](tmux/kill-the-current-session.md)
|
- [Kill The Current Session](tmux/kill-the-current-session.md)
|
||||||
- [List All Key Bindings](tmux/list-all-key-bindings.md)
|
- [List All Key Bindings](tmux/list-all-key-bindings.md)
|
||||||
- [List Processes Running Across All Session](tmux/list-processes-running-across-all-sessions.md)
|
|
||||||
- [List Sessions](tmux/list-sessions.md)
|
- [List Sessions](tmux/list-sessions.md)
|
||||||
- [Open New Splits To The Current Directory](tmux/open-new-splits-to-the-current-directory.md)
|
- [Open New Splits To The Current Directory](tmux/open-new-splits-to-the-current-directory.md)
|
||||||
- [Open New Window With A Specific Directory](tmux/open-new-window-with-a-specific-directory.md)
|
- [Open New Window With A Specific Directory](tmux/open-new-window-with-a-specific-directory.md)
|
||||||
@@ -1659,7 +1483,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Reset An Option Back To Its Default Value](tmux/reset-an-option-back-to-its-default-value.md)
|
- [Reset An Option Back To Its Default Value](tmux/reset-an-option-back-to-its-default-value.md)
|
||||||
- [Set Environment Variables When Creating Session](tmux/set-environment-variables-when-creating-session.md)
|
- [Set Environment Variables When Creating Session](tmux/set-environment-variables-when-creating-session.md)
|
||||||
- [Set Session Specific Environment Variables](tmux/set-session-specific-environment-variables.md)
|
- [Set Session Specific Environment Variables](tmux/set-session-specific-environment-variables.md)
|
||||||
- [Set Up Forwarding Prefix For Nested Session](tmux/set-up-forwarding-prefix-for-nested-session.md)
|
|
||||||
- [Show The Current Value For An Option](tmux/show-the-current-value-for-an-option.md)
|
- [Show The Current Value For An Option](tmux/show-the-current-value-for-an-option.md)
|
||||||
- [Swap Split Panes](tmux/swap-split-panes.md)
|
- [Swap Split Panes](tmux/swap-split-panes.md)
|
||||||
- [Switch To A Specific Session And Window](tmux/switch-to-a-specific-session-and-window.md)
|
- [Switch To A Specific Session And Window](tmux/switch-to-a-specific-session-and-window.md)
|
||||||
@@ -1693,9 +1516,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
### Unix
|
### Unix
|
||||||
|
|
||||||
- [All The Environment Variables](unix/all-the-environment-variables.md)
|
- [All The Environment Variables](unix/all-the-environment-variables.md)
|
||||||
- [Apply Successive Filters To Lines In Less](unix/apply-successive-filters-to-lines-in-less.md)
|
|
||||||
- [Authorize A cURL Request](unix/authorize-a-curl-request.md)
|
|
||||||
- [Browse And Search Help Docs](unix/browse-and-search-help-docs.md)
|
|
||||||
- [Cat A File With Line Numbers](unix/cat-a-file-with-line-numbers.md)
|
- [Cat A File With Line Numbers](unix/cat-a-file-with-line-numbers.md)
|
||||||
- [Cat Files With Color Using Bat](unix/cat-files-with-color-using-bat.md)
|
- [Cat Files With Color Using Bat](unix/cat-files-with-color-using-bat.md)
|
||||||
- [Change Default Shell For A User](unix/change-default-shell-for-a-user.md)
|
- [Change Default Shell For A User](unix/change-default-shell-for-a-user.md)
|
||||||
@@ -1707,7 +1527,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Check The Current Working Directory](unix/check-the-current-working-directory.md)
|
- [Check The Current Working Directory](unix/check-the-current-working-directory.md)
|
||||||
- [Check The Installed OpenSSL Version](unix/check-the-installed-openssl-version.md)
|
- [Check The Installed OpenSSL Version](unix/check-the-installed-openssl-version.md)
|
||||||
- [Clear The Screen](unix/clear-the-screen.md)
|
- [Clear The Screen](unix/clear-the-screen.md)
|
||||||
- [Combine All My TILs Into A Single File](unix/combine-all-my-tils-into-a-single-file.md)
|
|
||||||
- [Command Line Length Limitations](unix/command-line-length-limitations.md)
|
- [Command Line Length Limitations](unix/command-line-length-limitations.md)
|
||||||
- [Compare Two Variables In A Bash Script](unix/compare-two-variables-in-a-bash-script.md)
|
- [Compare Two Variables In A Bash Script](unix/compare-two-variables-in-a-bash-script.md)
|
||||||
- [Configure cd To Behave Like pushd In Zsh](unix/configure-cd-to-behave-like-pushd-in-zsh.md)
|
- [Configure cd To Behave Like pushd In Zsh](unix/configure-cd-to-behave-like-pushd-in-zsh.md)
|
||||||
@@ -1720,18 +1539,13 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Count The Number Of ripgrep Pattern Matches](unix/count-the-number-of-ripgrep-pattern-matches.md)
|
- [Count The Number Of ripgrep Pattern Matches](unix/count-the-number-of-ripgrep-pattern-matches.md)
|
||||||
- [Count The Number Of Words On A Webpage](unix/count-the-number-of-words-on-a-webpage.md)
|
- [Count The Number Of Words On A Webpage](unix/count-the-number-of-words-on-a-webpage.md)
|
||||||
- [Create A File Descriptor with Process Substitution](unix/create-a-file-descriptor-with-process-substitution.md)
|
- [Create A File Descriptor with Process Substitution](unix/create-a-file-descriptor-with-process-substitution.md)
|
||||||
- [Create A Filename With The Current Date](unix/create-a-filename-with-the-current-date.md)
|
|
||||||
- [Create A Sequence Of Values With A Step](unix/create-a-sequence-of-values-with-a-step.md)
|
- [Create A Sequence Of Values With A Step](unix/create-a-sequence-of-values-with-a-step.md)
|
||||||
- [Curl With Cookies](unix/curl-with-cookies.md)
|
- [Curl With Cookies](unix/curl-with-cookies.md)
|
||||||
- [Curling For Headers](unix/curling-for-headers.md)
|
- [Curling For Headers](unix/curling-for-headers.md)
|
||||||
- [Curling With Basic Auth Credentials](unix/curling-with-basic-auth-credentials.md)
|
- [Curling With Basic Auth Credentials](unix/curling-with-basic-auth-credentials.md)
|
||||||
- [Deduplicate List While Preserving Original Order](unix/deduplicate-list-while-preserving-original-order.md)
|
|
||||||
- [Determine ipv4 And ipv6 Public IP Addresses](unix/determine-ipv4-and-ipv6-public-ip-addresses.md)
|
|
||||||
- [Diff Two Files In Unified Format](unix/diff-two-files-in-unified-format.md)
|
|
||||||
- [Different Ways To Generate A v4 UUID](unix/different-ways-to-generate-a-v4-uuid.md)
|
- [Different Ways To Generate A v4 UUID](unix/different-ways-to-generate-a-v4-uuid.md)
|
||||||
- [Display All The Terminal Colors](unix/display-all-the-terminal-colors.md)
|
- [Display All The Terminal Colors](unix/display-all-the-terminal-colors.md)
|
||||||
- [Display Free Disk Space](unix/display-free-disk-space.md)
|
- [Display Free Disk Space](unix/display-free-disk-space.md)
|
||||||
- [Display Line Numbers While Using Less](unix/display-line-numbers-while-using-less.md)
|
|
||||||
- [Display The Contents Of A Directory As A Tree](unix/display-the-contents-of-a-directory-as-a-tree.md)
|
- [Display The Contents Of A Directory As A Tree](unix/display-the-contents-of-a-directory-as-a-tree.md)
|
||||||
- [Do A Dry Run Of An rsync](unix/do-a-dry-run-of-an-rsync.md)
|
- [Do A Dry Run Of An rsync](unix/do-a-dry-run-of-an-rsync.md)
|
||||||
- [Do Not Overwrite Existing Files](unix/do-not-overwrite-existing-files.md)
|
- [Do Not Overwrite Existing Files](unix/do-not-overwrite-existing-files.md)
|
||||||
@@ -1739,7 +1553,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Enable Multi-Select Of Results With fzf](unix/enable-multi-select-of-results-with-fzf.md)
|
- [Enable Multi-Select Of Results With fzf](unix/enable-multi-select-of-results-with-fzf.md)
|
||||||
- [Exclude A Command From The ZSH History File](unix/exclude-a-command-from-the-zsh-history-file.md)
|
- [Exclude A Command From The ZSH History File](unix/exclude-a-command-from-the-zsh-history-file.md)
|
||||||
- [Exclude A Directory With Find](unix/exclude-a-directory-with-find.md)
|
- [Exclude A Directory With Find](unix/exclude-a-directory-with-find.md)
|
||||||
- [Exclude A Specific File From fd Results](unix/exclude-a-specific-file-from-fd-results.md)
|
|
||||||
- [Exclude Certain Files From An rsync Run](unix/exclude-certain-files-from-an-rsync-run.md)
|
- [Exclude Certain Files From An rsync Run](unix/exclude-certain-files-from-an-rsync-run.md)
|
||||||
- [Figure Out The Week Of The Year From The Terminal](unix/figure-out-the-week-of-the-year-from-the-terminal.md)
|
- [Figure Out The Week Of The Year From The Terminal](unix/figure-out-the-week-of-the-year-from-the-terminal.md)
|
||||||
- [File Type Info With File](unix/file-type-info-with-file.md)
|
- [File Type Info With File](unix/file-type-info-with-file.md)
|
||||||
@@ -1757,10 +1570,8 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Fix Previous Command With fc](unix/fix-previous-command-with-fc.md)
|
- [Fix Previous Command With fc](unix/fix-previous-command-with-fc.md)
|
||||||
- [Fix Shim Path After asdf Upgrade](unix/fix-shim-path-after-asdf-upgrade.md)
|
- [Fix Shim Path After asdf Upgrade](unix/fix-shim-path-after-asdf-upgrade.md)
|
||||||
- [Fix Unlinked Node Binaries With asdf](unix/fix-unlinked-node-binaries-with-asdf.md)
|
- [Fix Unlinked Node Binaries With asdf](unix/fix-unlinked-node-binaries-with-asdf.md)
|
||||||
- [Format And Display Small Amounts Of Columnar Data](unix/format-and-display-small-amounts-of-columnar-data.md)
|
|
||||||
- [Forward Multiple Ports Over SSH](unix/forward-multiple-ports-over-ssh.md)
|
- [Forward Multiple Ports Over SSH](unix/forward-multiple-ports-over-ssh.md)
|
||||||
- [Generate A SAML Key And Certificate Pair](unix/generate-a-saml-key-and-certificate-pair.md)
|
- [Generate A SAML Key And Certificate Pair](unix/generate-a-saml-key-and-certificate-pair.md)
|
||||||
- [Generate A Sequence Of Numbered Items](unix/generate-a-sequence-of-numbered-items.md)
|
|
||||||
- [Generate Base64 Encoding Without Newlines](unix/generate-base64-encoding-without-newlines.md)
|
- [Generate Base64 Encoding Without Newlines](unix/generate-base64-encoding-without-newlines.md)
|
||||||
- [Generate Random 20-Character Hex String](unix/generate-random-20-character-hex-string.md)
|
- [Generate Random 20-Character Hex String](unix/generate-random-20-character-hex-string.md)
|
||||||
- [Get A List Of Locales On Your System](unix/get-a-list-of-locales-on-your-system.md)
|
- [Get A List Of Locales On Your System](unix/get-a-list-of-locales-on-your-system.md)
|
||||||
@@ -1775,15 +1586,12 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Grep For Files Without A Match](unix/grep-for-files-without-a-match.md)
|
- [Grep For Files Without A Match](unix/grep-for-files-without-a-match.md)
|
||||||
- [Grep For Files With Multiple Matches](unix/grep-for-files-with-multiple-matches.md)
|
- [Grep For Files With Multiple Matches](unix/grep-for-files-with-multiple-matches.md)
|
||||||
- [Grep For Multiple Patterns](unix/grep-for-multiple-patterns.md)
|
- [Grep For Multiple Patterns](unix/grep-for-multiple-patterns.md)
|
||||||
- [Have Script ShellCheck Itself When Executing](unix/have-script-shellcheck-itself-when-executing.md)
|
|
||||||
- [Hexdump A Compiled File](unix/hexdump-a-compiled-file.md)
|
- [Hexdump A Compiled File](unix/hexdump-a-compiled-file.md)
|
||||||
- [Ignore A Directory During ripgrep Search](unix/ignore-a-directory-during-ripgrep-search.md)
|
- [Ignore A Directory During ripgrep Search](unix/ignore-a-directory-during-ripgrep-search.md)
|
||||||
- [Ignore The Alias When Running A Command](unix/ignore-the-alias-when-running-a-command.md)
|
- [Ignore The Alias When Running A Command](unix/ignore-the-alias-when-running-a-command.md)
|
||||||
- [Include Ignore Files In Ripgrep Search](unix/include-ignore-files-in-ripgrep-search.md)
|
- [Include Ignore Files In Ripgrep Search](unix/include-ignore-files-in-ripgrep-search.md)
|
||||||
- [Inspect EXIF Data For An Image File](unix/inspect-exif-data-for-an-image-file.md)
|
|
||||||
- [Interactively Browse Available Node Versions](unix/interactively-browse-availabile-node-versions.md)
|
- [Interactively Browse Available Node Versions](unix/interactively-browse-availabile-node-versions.md)
|
||||||
- [Interactively Switch asdf Package Versions](unix/interactively-switch-asdf-package-versions.md)
|
- [Interactively Switch asdf Package Versions](unix/interactively-switch-asdf-package-versions.md)
|
||||||
- [Interpret Cron Schedule From The CLI](unix/interpret-cron-schedule-from-the-cli.md)
|
|
||||||
- [Jump To The Ends Of Your Shell History](unix/jump-to-the-ends-of-your-shell-history.md)
|
- [Jump To The Ends Of Your Shell History](unix/jump-to-the-ends-of-your-shell-history.md)
|
||||||
- [Kill Everything Running On A Certain Port](unix/kill-everything-running-on-a-certain-port.md)
|
- [Kill Everything Running On A Certain Port](unix/kill-everything-running-on-a-certain-port.md)
|
||||||
- [Killing A Frozen SSH Session](unix/killing-a-frozen-ssh-session.md)
|
- [Killing A Frozen SSH Session](unix/killing-a-frozen-ssh-session.md)
|
||||||
@@ -1806,7 +1614,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Load Env Vars In Bash Script](unix/load-env-vars-in-bash-script.md)
|
- [Load Env Vars In Bash Script](unix/load-env-vars-in-bash-script.md)
|
||||||
- [Look Through All Files That Have Been Git Stashed](unix/look-through-all-files-that-have-been-git-stashed.md)
|
- [Look Through All Files That Have Been Git Stashed](unix/look-through-all-files-that-have-been-git-stashed.md)
|
||||||
- [Make Direnv Less Noisy](unix/make-direnv-less-noisy.md)
|
- [Make Direnv Less Noisy](unix/make-direnv-less-noisy.md)
|
||||||
- [Make Neovim The Default Way To View Man Pages](unix/make-neovim-the-default-way-to-view-man-pages.md)
|
|
||||||
- [Manually Pass Two Git Files To Delta](unix/manually-pass-two-git-files-to-delta.md)
|
- [Manually Pass Two Git Files To Delta](unix/manually-pass-two-git-files-to-delta.md)
|
||||||
- [Map A Domain To localhost](unix/map-a-domain-to-localhost.md)
|
- [Map A Domain To localhost](unix/map-a-domain-to-localhost.md)
|
||||||
- [Negative Look-Ahead Search With ripgrep](unix/negative-look-ahead-search-with-ripgrep.md)
|
- [Negative Look-Ahead Search With ripgrep](unix/negative-look-ahead-search-with-ripgrep.md)
|
||||||
@@ -1819,16 +1626,13 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Print A Range Of Lines For A File With Bat](unix/print-a-range-of-lines-for-a-file-with-bat.md)
|
- [Print A Range Of Lines For A File With Bat](unix/print-a-range-of-lines-for-a-file-with-bat.md)
|
||||||
- [Print DateTime Represented By Unix Timestamp](unix/print-datetime-represented-by-unix-timestamp.md)
|
- [Print DateTime Represented By Unix Timestamp](unix/print-datetime-represented-by-unix-timestamp.md)
|
||||||
- [Print Milliseconds In Human-Readable Format](unix/print-milliseconds-in-human-readable-format.md)
|
- [Print Milliseconds In Human-Readable Format](unix/print-milliseconds-in-human-readable-format.md)
|
||||||
- [Print Out File With Bat Without Formatting](unix/print-out-file-with-bat-without-formatting.md)
|
|
||||||
- [Print Out Files In Reverse](unix/print-out-files-in-reverse.md)
|
- [Print Out Files In Reverse](unix/print-out-files-in-reverse.md)
|
||||||
- [Print The Current Date In Human-Readable Format](unix/print-the-current-date-in-human-readable-format.md)
|
- [Print The Current Date In Human-Readable Format](unix/print-the-current-date-in-human-readable-format.md)
|
||||||
- [Produce A Lowercase V4 UUID](unix/produce-a-lowercase-v4-uuid.md)
|
- [Produce A Lowercase V4 UUID](unix/produce-a-lowercase-v4-uuid.md)
|
||||||
- [Provide A Fallback Value For Unset Parameter](unix/provide-a-fallback-value-for-unset-parameter.md)
|
- [Provide A Fallback Value For Unset Parameter](unix/provide-a-fallback-value-for-unset-parameter.md)
|
||||||
- [Remove A Directory Called `-p`](unix/remove-a-directory-called-dash-p.md)
|
- [Remove A Directory Called `-p`](unix/remove-a-directory-called-dash-p.md)
|
||||||
- [Rename A Bunch Of Files By Constructing mv Commands](unix/rename-a-bunch-of-files-by-constructing-mv-commands.md)
|
|
||||||
- [Repeat Yourself](unix/repeat-yourself.md)
|
- [Repeat Yourself](unix/repeat-yourself.md)
|
||||||
- [Replace Pattern Across Many Files In A Project](unix/replace-pattern-across-many-files-in-a-project.md)
|
- [Replace Pattern Across Many Files In A Project](unix/replace-pattern-across-many-files-in-a-project.md)
|
||||||
- [Reverse Each Line Of A File](unix/reverse-each-line-of-a-file.md)
|
|
||||||
- [Run A Command Repeatedly Several Times](unix/run-a-command-repeatedly-several-times.md)
|
- [Run A Command Repeatedly Several Times](unix/run-a-command-repeatedly-several-times.md)
|
||||||
- [Run A cURL Command Without The Progress Meter](unix/run-a-curl-command-without-the-progress-meter.md)
|
- [Run A cURL Command Without The Progress Meter](unix/run-a-curl-command-without-the-progress-meter.md)
|
||||||
- [Safely Edit The Sudoers File With Vim](unix/safely-edit-the-sudoers-file-with-vim.md)
|
- [Safely Edit The Sudoers File With Vim](unix/safely-edit-the-sudoers-file-with-vim.md)
|
||||||
@@ -1840,11 +1644,9 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Securely Remove Files](unix/securely-remove-files.md)
|
- [Securely Remove Files](unix/securely-remove-files.md)
|
||||||
- [See Where asdf Gets Current Tool Version](unix/see-where-asdf-gets-current-tool-version.md)
|
- [See Where asdf Gets Current Tool Version](unix/see-where-asdf-gets-current-tool-version.md)
|
||||||
- [Set The asdf Package Version For A Single Shell](unix/set-the-asdf-package-version-for-a-single-shell.md)
|
- [Set The asdf Package Version For A Single Shell](unix/set-the-asdf-package-version-for-a-single-shell.md)
|
||||||
- [Shorten SSH Commands With Aliases](unix/shorten-ssh-commands-with-aliases.md)
|
|
||||||
- [Show A File Preview When Searching With FZF](unix/show-a-file-preview-when-searching-with-fzf.md)
|
- [Show A File Preview When Searching With FZF](unix/show-a-file-preview-when-searching-with-fzf.md)
|
||||||
- [Show Disk Usage For The Current Directory](unix/show-disk-usage-for-the-current-directory.md)
|
- [Show Disk Usage For The Current Directory](unix/show-disk-usage-for-the-current-directory.md)
|
||||||
- [Show The Size Of Everything In A Directory](unix/show-the-size-of-everything-in-a-directory.md)
|
- [Show The Size Of Everything In A Directory](unix/show-the-size-of-everything-in-a-directory.md)
|
||||||
- [Show Tree View Of Processes And Subprocesses](unix/show-tree-view-of-processes-and-subprocesses.md)
|
|
||||||
- [Skip Paging If Output Fits On Screen With Less](unix/skip-paging-if-output-fits-on-screen-with-less.md)
|
- [Skip Paging If Output Fits On Screen With Less](unix/skip-paging-if-output-fits-on-screen-with-less.md)
|
||||||
- [SSH Escape Sequences](unix/ssh-escape-sequences.md)
|
- [SSH Escape Sequences](unix/ssh-escape-sequences.md)
|
||||||
- [SSH With Port Forwarding](unix/ssh-with-port-forwarding.md)
|
- [SSH With Port Forwarding](unix/ssh-with-port-forwarding.md)
|
||||||
@@ -1853,16 +1655,13 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Switch Versions of a Brew Formula](unix/switch-versions-of-a-brew-formula.md)
|
- [Switch Versions of a Brew Formula](unix/switch-versions-of-a-brew-formula.md)
|
||||||
- [Tell direnv To Load The Env File](unix/tell-direnv-to-load-the-env-file.md)
|
- [Tell direnv To Load The Env File](unix/tell-direnv-to-load-the-env-file.md)
|
||||||
- [Touch Access And Modify Times Individually](unix/touch-access-and-modify-times-individually.md)
|
- [Touch Access And Modify Times Individually](unix/touch-access-and-modify-times-individually.md)
|
||||||
- [Transform Text To Lowercase](unix/transform-text-to-lowercase.md)
|
|
||||||
- [Type Fewer Paths With Brace Expansion](unix/type-fewer-paths-with-brace-expansion.md)
|
- [Type Fewer Paths With Brace Expansion](unix/type-fewer-paths-with-brace-expansion.md)
|
||||||
- [Undo Changes Made To Current Terminal Prompt](unix/undo-changes-made-to-current-terminal-prompt.md)
|
- [Undo Changes Made To Current Terminal Prompt](unix/undo-changes-made-to-current-terminal-prompt.md)
|
||||||
- [Undo Some Command Line Editing](unix/undo-some-command-line-editing.md)
|
- [Undo Some Command Line Editing](unix/undo-some-command-line-editing.md)
|
||||||
- [Unrestrict Where ripgrep Searches](unix/unrestrict-where-ripgrep-searches.md)
|
- [Unrestrict Where ripgrep Searches](unix/unrestrict-where-ripgrep-searches.md)
|
||||||
- [Update Package Versions Known By asdf Plugin](unix/update-package-versions-known-by-asdf-plugin.md)
|
- [Update Package Versions Known By asdf Plugin](unix/update-package-versions-known-by-asdf-plugin.md)
|
||||||
- [Use fzf To Change Directories](unix/use-fzf-to-change-directories.md)
|
- [Use fzf To Change Directories](unix/use-fzf-to-change-directories.md)
|
||||||
- [Use Negative Lookbehind Matching With ripgrep](unix/use-negative-lookbehind-matching-with-ripgrep.md)
|
|
||||||
- [Use Regex Pattern Matching With Grep](unix/use-regex-pattern-matching-with-grep.md)
|
- [Use Regex Pattern Matching With Grep](unix/use-regex-pattern-matching-with-grep.md)
|
||||||
- [Use The Readline Keybindings Anywhere](unix/use-the-readline-keybindings-anywhere.md)
|
|
||||||
- [View A Web Page In The Terminal](unix/view-a-web-page-in-the-terminal.md)
|
- [View A Web Page In The Terminal](unix/view-a-web-page-in-the-terminal.md)
|
||||||
- [View The Source For A Brew Formula](unix/view-the-source-for-a-brew-formula.md)
|
- [View The Source For A Brew Formula](unix/view-the-source-for-a-brew-formula.md)
|
||||||
- [Watch The Difference](unix/watch-the-difference.md)
|
- [Watch The Difference](unix/watch-the-difference.md)
|
||||||
@@ -1897,7 +1696,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Breaking The Undo Sequence](vim/breaking-the-undo-sequence.md)
|
- [Breaking The Undo Sequence](vim/breaking-the-undo-sequence.md)
|
||||||
- [Buffer Time Travel](vim/buffer-time-travel.md)
|
- [Buffer Time Travel](vim/buffer-time-travel.md)
|
||||||
- [Build And Install A Go Program](vim/build-and-install-a-go-program.md)
|
- [Build And Install A Go Program](vim/build-and-install-a-go-program.md)
|
||||||
- [Bypass On-Save Tooling When Writing File](vim/bypass-on-save-tooling-when-writing-file.md)
|
|
||||||
- [Case-Aware Substitution With vim-abolish](vim/case-aware-substitution-with-vim-abolish.md)
|
- [Case-Aware Substitution With vim-abolish](vim/case-aware-substitution-with-vim-abolish.md)
|
||||||
- [Case-Insensitive Substitution](vim/case-insensitive-substitution.md)
|
- [Case-Insensitive Substitution](vim/case-insensitive-substitution.md)
|
||||||
- [Center The Cursor](vim/center-the-cursor.md)
|
- [Center The Cursor](vim/center-the-cursor.md)
|
||||||
@@ -2000,7 +1798,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Replace A Character](vim/replace-a-character.md)
|
- [Replace A Character](vim/replace-a-character.md)
|
||||||
- [Reset Target tslime Pane](vim/reset-target-tslime-pane.md)
|
- [Reset Target tslime Pane](vim/reset-target-tslime-pane.md)
|
||||||
- [Reverse A Group Of Lines](vim/reverse-a-group-of-lines.md)
|
- [Reverse A Group Of Lines](vim/reverse-a-group-of-lines.md)
|
||||||
- [Reword A Commit Message With Fugitive](vim/reword-a-commit-message-with-fugitive.md)
|
|
||||||
- [Rotate Everything By 13 Letters](vim/rotate-everything-by-13-letters.md)
|
- [Rotate Everything By 13 Letters](vim/rotate-everything-by-13-letters.md)
|
||||||
- [Rotate The Orientation Of Split Windows](vim/rotate-the-orientation-of-split-windows.md)
|
- [Rotate The Orientation Of Split Windows](vim/rotate-the-orientation-of-split-windows.md)
|
||||||
- [Running Bundle With vim-bundler](vim/running-bundle-with-vim-bundler.md)
|
- [Running Bundle With vim-bundler](vim/running-bundle-with-vim-bundler.md)
|
||||||
@@ -2049,7 +1846,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Find The Location Of User Settings JSON File](vscode/find-the-location-of-user-settings-json-file.md)
|
- [Find The Location Of User Settings JSON File](vscode/find-the-location-of-user-settings-json-file.md)
|
||||||
- [Jump To Problems In The Current File](vscode/jump-to-problems-in-the-current-file.md)
|
- [Jump To Problems In The Current File](vscode/jump-to-problems-in-the-current-file.md)
|
||||||
- [Open An Integrated Terminal Window](vscode/open-an-integrated-terminal-window.md)
|
- [Open An Integrated Terminal Window](vscode/open-an-integrated-terminal-window.md)
|
||||||
- [Open File On Remote Like GitHub](vscode/open-file-on-remote-like-github.md)
|
|
||||||
- [Pop Open The Quick Fix Window](vscode/pop-open-the-quick-fix-window.md)
|
- [Pop Open The Quick Fix Window](vscode/pop-open-the-quick-fix-window.md)
|
||||||
- [Step Through Project-Wide Search Results](vscode/step-through-project-wide-search-results.md)
|
- [Step Through Project-Wide Search Results](vscode/step-through-project-wide-search-results.md)
|
||||||
- [Synchronize Vim Clipboard With System Clipboard](vscode/synchronize-vim-clipboard-with-system-clipboard.md)
|
- [Synchronize Vim Clipboard With System Clipboard](vscode/synchronize-vim-clipboard-with-system-clipboard.md)
|
||||||
@@ -2079,7 +1875,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Create A Local Sanity Dataset Backup](workflow/create-a-local-sanity-dataset-backup.md)
|
- [Create A Local Sanity Dataset Backup](workflow/create-a-local-sanity-dataset-backup.md)
|
||||||
- [Create A Public URL For A Local Server](workflow/create-a-public-url-for-a-local-server.md)
|
- [Create A Public URL For A Local Server](workflow/create-a-public-url-for-a-local-server.md)
|
||||||
- [Create Todo Items In Logseq](workflow/create-todo-items-in-logseq.md)
|
- [Create Todo Items In Logseq](workflow/create-todo-items-in-logseq.md)
|
||||||
- [Do Project Time Tracking From The CLI](workflow/do-project-time-tracking-from-the-cli.md)
|
|
||||||
- [Enable Dev Tools For Safari](workflow/enable-dev-tools-for-safari.md)
|
- [Enable Dev Tools For Safari](workflow/enable-dev-tools-for-safari.md)
|
||||||
- [Forward Stripe Events To Local Server](workflow/forward-stripe-events-to-local-server.md)
|
- [Forward Stripe Events To Local Server](workflow/forward-stripe-events-to-local-server.md)
|
||||||
- [Get URL For GitHub User Profile Photo](workflow/get-url-for-github-user-profile-photo.md)
|
- [Get URL For GitHub User Profile Photo](workflow/get-url-for-github-user-profile-photo.md)
|
||||||
@@ -2094,13 +1889,10 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Send A Message To A Discord Channel](workflow/send-a-message-to-a-discord-channel.md)
|
- [Send A Message To A Discord Channel](workflow/send-a-message-to-a-discord-channel.md)
|
||||||
- [Send A PDF To Your Kindle](workflow/send-a-pdf-to-your-kindle.md)
|
- [Send A PDF To Your Kindle](workflow/send-a-pdf-to-your-kindle.md)
|
||||||
- [Set Recurring Reminders In Slack](workflow/set-recurring-reminders-in-slack.md)
|
- [Set Recurring Reminders In Slack](workflow/set-recurring-reminders-in-slack.md)
|
||||||
- [Show All Linear Keyboard Shortcuts](workflow/show-all-linear-keyboard-shortcuts.md)
|
|
||||||
- [Show Linting Errors In Zed](workflow/show-linting-errors-in-zed.md)
|
- [Show Linting Errors In Zed](workflow/show-linting-errors-in-zed.md)
|
||||||
- [Temporarily Hide CleanShot X Capture Previews](workflow/temporarily-hide-cleanshot-x-capture-previews.md)
|
- [Temporarily Hide CleanShot X Capture Previews](workflow/temporarily-hide-cleanshot-x-capture-previews.md)
|
||||||
- [Toggle Between Stories In Storybook](workflow/toggle-between-stories-in-storybook.md)
|
- [Toggle Between Stories In Storybook](workflow/toggle-between-stories-in-storybook.md)
|
||||||
- [Update asdf Plugins With Latest Package Versions](workflow/update-asdf-plugins-with-latest-package-versions.md)
|
- [Update asdf Plugins With Latest Package Versions](workflow/update-asdf-plugins-with-latest-package-versions.md)
|
||||||
- [View A Nicely-Formatted CSV In Terminal](workflow/view-a-nicely-formatted-csv-in-terminal.md)
|
|
||||||
- [View Nicely Formatted Markdown From Terminal](workflow/view-nicely-formatted-markdown-from-terminal.md)
|
|
||||||
- [View The PR For The Current GitHub Branch](workflow/view-the-pr-for-the-current-github-branch.md)
|
- [View The PR For The Current GitHub Branch](workflow/view-the-pr-for-the-current-github-branch.md)
|
||||||
|
|
||||||
### XState
|
### XState
|
||||||
@@ -2135,7 +1927,6 @@ If you've learned something here, support my efforts writing daily TILs by
|
|||||||
- [Add To The Path Via Path Array](zsh/add-to-the-path-via-path-array.md)
|
- [Add To The Path Via Path Array](zsh/add-to-the-path-via-path-array.md)
|
||||||
- [Create And Jump Into A Directory](zsh/create-and-jump-into-a-directory.md)
|
- [Create And Jump Into A Directory](zsh/create-and-jump-into-a-directory.md)
|
||||||
- [Link A Scalar To An Array](zsh/link-a-scalar-to-an-array.md)
|
- [Link A Scalar To An Array](zsh/link-a-scalar-to-an-array.md)
|
||||||
- [List Available Zle Keybindings](zsh/list-available-zle-keybindings.md)
|
|
||||||
- [Use A Space To Exclude Command From History](zsh/use-a-space-to-exclude-command-from-history.md)
|
- [Use A Space To Exclude Command From History](zsh/use-a-space-to-exclude-command-from-history.md)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
@@ -2146,10 +1937,6 @@ current number of TILs and display the result in the command tray.
|
|||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
I've written more about how this repo came to be in [How I Built a Learning
|
|
||||||
Machine](https://dev.to/jbranchaud/how-i-built-a-learning-machine-45k9) and [A
|
|
||||||
Decade of TILs](https://www.visualmode.dev/a-decade-of-tils).
|
|
||||||
|
|
||||||
I shamelessly stole this idea from
|
I shamelessly stole this idea from
|
||||||
[thoughtbot/til](https://github.com/thoughtbot/til).
|
[thoughtbot/til](https://github.com/thoughtbot/til).
|
||||||
|
|
||||||
@@ -2161,7 +1948,7 @@ I shamelessly stole this idea from
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
© 2015-2026 Josh Branchaud
|
© 2015-2025 Josh Branchaud
|
||||||
|
|
||||||
This repository is licensed under the MIT license. See `LICENSE` for
|
This repository is licensed under the MIT license. See `LICENSE` for
|
||||||
details.
|
details.
|
||||||
|
|||||||
-103
@@ -1,103 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
vars:
|
|
||||||
NOTES_DIR: notes
|
|
||||||
NOTES_FILE: '{{.NOTES_DIR}}/NOTES.md'
|
|
||||||
EDITOR: '{{.EDITOR | default "nvim"}}'
|
|
||||||
|
|
||||||
tasks:
|
|
||||||
default:
|
|
||||||
desc: Show available commands
|
|
||||||
cmds:
|
|
||||||
- task --list
|
|
||||||
|
|
||||||
browse:list:
|
|
||||||
desc: Print deduped, newest-first TIL paths
|
|
||||||
silent: true
|
|
||||||
cmds:
|
|
||||||
- |
|
|
||||||
git log --diff-filter=A --name-only --pretty=format: -- '*/*.md' \
|
|
||||||
| grep -v '^$' \
|
|
||||||
| awk '!seen[$0]++'
|
|
||||||
|
|
||||||
browse:
|
|
||||||
desc: Pick from 5 most recent TILs (fzf) and open in browser
|
|
||||||
interactive: true
|
|
||||||
silent: true
|
|
||||||
cmds:
|
|
||||||
- |
|
|
||||||
FILE=$(task browse:list | head -5 | fzf --prompt="Open TIL: " --height=40% --reverse) || true
|
|
||||||
if [ -n "$FILE" ]; then
|
|
||||||
gh browse "$FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
browse:latest:
|
|
||||||
desc: Open the single most recent TIL in the browser
|
|
||||||
silent: true
|
|
||||||
cmds:
|
|
||||||
- |
|
|
||||||
FILE=$(task browse:list | head -1)
|
|
||||||
if [ -n "$FILE" ]; then
|
|
||||||
gh browse "$FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
notes:
|
|
||||||
desc: Interactive picker for notes tasks
|
|
||||||
cmds:
|
|
||||||
- |
|
|
||||||
TASK=$(task --list | grep "^\* notes:" | sed 's/^\* notes://' | sed 's/\s\+/ - /' | fzf --prompt="Select notes task: " --height=40% --reverse) || true
|
|
||||||
if [ -n "$TASK" ]; then
|
|
||||||
TASK_NAME=$(echo "$TASK" | awk '{print $1}' | sed 's/:$//')
|
|
||||||
task notes:$TASK_NAME
|
|
||||||
fi
|
|
||||||
interactive: true
|
|
||||||
silent: true
|
|
||||||
|
|
||||||
notes:edit:
|
|
||||||
desc: All-in-one edit, commit, and push notes
|
|
||||||
cmds:
|
|
||||||
- task notes:open
|
|
||||||
- task notes:push
|
|
||||||
|
|
||||||
notes:sync:
|
|
||||||
desc: Sync latest changes from the notes submodule
|
|
||||||
cmds:
|
|
||||||
- cd {{.NOTES_DIR}} && git checkout main && git pull
|
|
||||||
silent: false
|
|
||||||
|
|
||||||
notes:open:
|
|
||||||
desc: Opens NOTES.md (syncs latest changes first) in default editor
|
|
||||||
deps: [notes:sync]
|
|
||||||
cmds:
|
|
||||||
- $EDITOR {{.NOTES_FILE}}
|
|
||||||
interactive: true
|
|
||||||
|
|
||||||
notes:push:
|
|
||||||
desc: Commit and push changes to notes submodule
|
|
||||||
dir: '{{.NOTES_DIR}}'
|
|
||||||
cmds:
|
|
||||||
- git add NOTES.md
|
|
||||||
- git commit -m "Update notes - $(date '+%Y-%m-%d %H:%M')"
|
|
||||||
- git pull --rebase
|
|
||||||
- git push
|
|
||||||
status:
|
|
||||||
- git diff --exit-code NOTES.md
|
|
||||||
silent: false
|
|
||||||
|
|
||||||
notes:status:
|
|
||||||
desc: Check status of notes submodule
|
|
||||||
dir: '{{.NOTES_DIR}}'
|
|
||||||
cmds:
|
|
||||||
- git status
|
|
||||||
|
|
||||||
notes:diff:
|
|
||||||
desc: Show uncommitted changes in notes
|
|
||||||
dir: '{{.NOTES_DIR}}'
|
|
||||||
cmds:
|
|
||||||
- git diff NOTES.md
|
|
||||||
|
|
||||||
notes:log:
|
|
||||||
desc: Show recent commit history for notes
|
|
||||||
dir: '{{.NOTES_DIR}}'
|
|
||||||
cmds:
|
|
||||||
- git log --oneline -10
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# List RDS Snapshots With Matching Identifier Prefix
|
|
||||||
|
|
||||||
I'm working on a script that manually creates a snapshot which it will then
|
|
||||||
restore to a temporary database that I can scrub and dump. The snapshots that
|
|
||||||
this script takes are _manual_ and they are named with identifiers that have a
|
|
||||||
defining prefix (`dev-snapshot-`). Besides the few snapshots created by this
|
|
||||||
script, there are tons of automated snapshots that RDS creates for
|
|
||||||
backup/recovery purposes.
|
|
||||||
|
|
||||||
I want to list any snapshots that have been created by the script. I can do
|
|
||||||
this with the `describe-db-snapshots` command and some filters.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ aws rds describe-db-snapshots \
|
|
||||||
--snapshot-type manual \
|
|
||||||
--query "DBSnapshots[?starts_with(DBSnapshotIdentifier, 'dev-snapshot-')].DBSnapshotIdentifier" \
|
|
||||||
--no-cli-pager
|
|
||||||
|
|
||||||
[
|
|
||||||
"dev-snapshot-20250327-155355"
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
There are two key pieces. The `--snapshot-type manual` filter excludes all
|
|
||||||
those automated snapshots. The `--query` both filters to any snapshots whose
|
|
||||||
identifier `?starts_with` the prefix `dev-snapshot-` and then refines the
|
|
||||||
output to just the `DBSnapshotIdentifier` instead of the entire JSON object.
|
|
||||||
|
|
||||||
[source](https://docs.aws.amazon.com/cli/latest/reference/rds/describe-db-snapshots.html)
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# Edit The Current Command Prompt
|
|
||||||
|
|
||||||
A neat feature of `bash` is the ability to open whatever the current state of
|
|
||||||
the command prompt is into your default editor.
|
|
||||||
|
|
||||||
Let's say we have a really long command that we've just tried to run, but it
|
|
||||||
failed and we need to make a small change somewhere in the middle. Instead of
|
|
||||||
holding the left arrow key for 30 seconds, we can instead hit `CTRL-X CTRL-E`.
|
|
||||||
|
|
||||||
This pops us into our `EDITOR` (or maybe `VISUAL`, not sure which). In my case,
|
|
||||||
that is `nvim`. I now have access to all the features I'm used to in `nvim` for
|
|
||||||
quickly navigating to and editing, searching and replacing, or whatever.
|
|
||||||
|
|
||||||
Once I've got the command how I like it, I can save and exit (`:wq`) and the
|
|
||||||
updated command will be executed.
|
|
||||||
|
|
||||||
This is similar to [the `fc` builtin](unix/fix-previous-command-with-fc.md),
|
|
||||||
which also happens to be available for `zsh`.
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Clean Up Your Brew Installations
|
|
||||||
|
|
||||||
Over time as you upgrade brew-installed programs and make changes to your
|
|
||||||
`Brewfile`, your machine will have artifacts left behind that you no longer
|
|
||||||
need.
|
|
||||||
|
|
||||||
Periodically, it is good to clean things up.
|
|
||||||
|
|
||||||
First, you can get a summary of stale and outdated files that brew has
|
|
||||||
installed. Use the `--dry-run` flag.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ brew cleanup --dry-run
|
|
||||||
```
|
|
||||||
|
|
||||||
If you feel good about what you see in the output, then give things a clean.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ brew cleanup
|
|
||||||
```
|
|
||||||
|
|
||||||
Second, if you are using a `Brewfile` to manage what `brew` installs, then you
|
|
||||||
can instruct `brew` to uninstall any dependencies that aren't specified in that
|
|
||||||
file.
|
|
||||||
|
|
||||||
By default it operates as a dry run and the `--force` flag will be needed to
|
|
||||||
actually do the cleanup. And specify the filename if it doesn't match the
|
|
||||||
default of `Brewfile`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ brew bundle cleanup --file=Brewfile.personal
|
|
||||||
```
|
|
||||||
|
|
||||||
If the output looks good, then force the cleanup:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ brew bundle cleanup --force --file=Brewfile.personal
|
|
||||||
```
|
|
||||||
|
|
||||||
See `brew cleanup --help` and `brew bundle --help` for more details.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Install From Nonstandard Brewfile
|
|
||||||
|
|
||||||
When you want to install the packages listed in the `Brewfile` for your current
|
|
||||||
project (or dotfiles), you can run:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ brew bundle
|
|
||||||
```
|
|
||||||
|
|
||||||
And `brew` knows to look for and use the `Brewfile` in the current directory.
|
|
||||||
|
|
||||||
If, however, you are trying to run `brew bundle` for a `Brewfile` located
|
|
||||||
somewhere besides the current directory *OR* you want to target a file with a
|
|
||||||
non-standard name (like
|
|
||||||
[`Brewfile.personal`](https://github.com/jbranchaud/dotfiles/blob/main/Brewfile.personal)),
|
|
||||||
then you can use the `--file` flag.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ brew bundle --file Brewfile.personal
|
|
||||||
```
|
|
||||||
|
|
||||||
This is what I do [here in my `dotfiles`
|
|
||||||
repo](https://github.com/jbranchaud/dotfiles/blob/b053f6251cae7ed52f698fc2a2c40ba82c5881b0/installer/mac-setup.sh#L42-L48).
|
|
||||||
|
|
||||||
See `man brew` and find the section on `brew bundle` for more details.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Install Go Packages In Brewfile
|
|
||||||
|
|
||||||
Typically my `Brewfile` is only full of `brew` and `cask` directives. That's
|
|
||||||
starting to change now that `brew` supports installing Go packages listed in the
|
|
||||||
`Brewfile`.
|
|
||||||
|
|
||||||
Use the `go` directive and the URL to the hosted Go package.
|
|
||||||
|
|
||||||
Here is an example of a `Brewfile` that includes a `cask`, `brew`, and `go`
|
|
||||||
directive.
|
|
||||||
|
|
||||||
```
|
|
||||||
# screen resolution tool
|
|
||||||
cask "betterdisplay"
|
|
||||||
|
|
||||||
# Mac keychain management, gpg key
|
|
||||||
brew "pinentry-mac"
|
|
||||||
|
|
||||||
# Sanitized production Postgres dumps
|
|
||||||
go "github.com/jackc/pg_partialcopy"
|
|
||||||
```
|
|
||||||
|
|
||||||
I've recently added the exact package from above to my [`dotfiles`
|
|
||||||
repo](https://github.com/jbranchaud/dotfiles/commit/e83e9d19504f0e2f95eba33123f907f999bf865e).
|
|
||||||
|
|
||||||
Here is the [PR to `brew`](https://github.com/Homebrew/brew/pull/20798) where
|
|
||||||
this functionality was added back in October of 2025.
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# Open Current Tab In New Window With Vimium
|
|
||||||
|
|
||||||
Sometime I have a busy Chrome window going with a bunch of tabs open for
|
|
||||||
various lines of work as well as a number of tabs that I've neglected to close.
|
|
||||||
I then open a new tab, find something useful, and realize I'm at a "branching
|
|
||||||
point". I'm about to start in on a specific chunk of work that will probably
|
|
||||||
involve opening several more tabs and switch back and forth between some
|
|
||||||
dashboards. I want to start all of this from a fresh slate -- or at least from
|
|
||||||
a fresh Chrome window.
|
|
||||||
|
|
||||||
With [Vimium](https://github.com/philc/vimium), I can hit `W` (`Shift-w`) to
|
|
||||||
have the current tab move from the current window to a new window. The original
|
|
||||||
window, minus that one tab, will be left as is so that I can go back to it as
|
|
||||||
needed.
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# Allow Edits From The Start
|
|
||||||
|
|
||||||
A common pattern for me when using Claude Code is that I start it up in a
|
|
||||||
project, I prompt it with a question or feature spec, it either comes up with a
|
|
||||||
plan or just starts working, and as soon as it is ready to make its first edits
|
|
||||||
to a file, it prompts me something like:
|
|
||||||
|
|
||||||
```
|
|
||||||
Do you want to make this edit to Taskfile.yml?
|
|
||||||
❯ 1. Yes
|
|
||||||
2. Yes, allow all edits during this session (shift+tab)
|
|
||||||
3. Type here to tell Claude what to do differently
|
|
||||||
```
|
|
||||||
|
|
||||||
That's a nice default so that I don't get surprised by Claude Code editing a
|
|
||||||
bunch of files.
|
|
||||||
|
|
||||||
However, if I'm in a git-backed project and I'm going into a session intending
|
|
||||||
to make edits, then I can skip the formalities. I can tell Claude Code when
|
|
||||||
starting up the session that edits are allowed.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ claude --permission-mode acceptEdits
|
|
||||||
```
|
|
||||||
|
|
||||||
When I do this, I'll see the following indicator below the prompt input field:
|
|
||||||
|
|
||||||
```
|
|
||||||
⏵⏵ accept edits on (shift+tab to cycle)
|
|
||||||
```
|
|
||||||
|
|
||||||
If I've already started `claude` but I forgot to specify that permission mode, I
|
|
||||||
can also toggle right into _accept edits_ by hitting `Shift+Tab`.
|
|
||||||
|
|
||||||
[source](https://www.youtube.com/watch?v=_IK18goX4X8)
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Distinguish Sessions With Different Colors
|
|
||||||
|
|
||||||
I sometimes have several Claude Code sessions open at once. As I bounce between
|
|
||||||
tmux windows, it can sometimes be tricky to tell them apart at a glance. One way
|
|
||||||
that Claude Code can help with this is with some light styling. You can change
|
|
||||||
the accent color of a session with the `/color` command.
|
|
||||||
|
|
||||||
Run it as is and it will choose a random color to set the session to.
|
|
||||||
|
|
||||||
Or you can pick from any of the available colors which it will give you a hint
|
|
||||||
for if you type a space after `/color`.
|
|
||||||
|
|
||||||
```
|
|
||||||
/color [red|blue|green|yellow|purple|orange|pink|cyan|default]
|
|
||||||
```
|
|
||||||
|
|
||||||
I can run the following to set it to cyan:
|
|
||||||
|
|
||||||
```
|
|
||||||
/color cyan
|
|
||||||
```
|
|
||||||
|
|
||||||
More details on this kinds of commands can be found in the [_Commands_
|
|
||||||
docs](https://code.claude.com/docs/en/commands).
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# Monitor Usage Limits From CLI
|
|
||||||
|
|
||||||
When I first started using Claude Code enough to push the usage limits, I would
|
|
||||||
periodically switch over to the browser to check
|
|
||||||
`https://claude.ai/settings/usage` to see how close I was getting. That page
|
|
||||||
would tell me what percentage of my allotted usage I had consumed so far for the
|
|
||||||
current 5-hour session and then how long until that 5-hour usage window resets.
|
|
||||||
|
|
||||||
This can also be viewed directly in Claude Code for the CLI.
|
|
||||||
|
|
||||||
First, run the `/status` slash command and then _tab_ over to the _Usage_
|
|
||||||
section. There you will see the same details as in the web view.
|
|
||||||
|
|
||||||
I'm also learned, as I write this, that you can go directly to the _Usage_
|
|
||||||
section by typing the `/usage` slash command.
|
|
||||||
|
|
||||||
See [the docs](https://code.claude.com/docs/en/slash-commands) for a listing of
|
|
||||||
all slash commands.
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Open Current Prompt In Default Editor
|
|
||||||
|
|
||||||
[Claude Code](https://www.claude.com/product/claude-code) gives you a single
|
|
||||||
line to write a prompt. You can write and write as much as you want, but it will
|
|
||||||
all be on that single line. And avoid accidentally hitting 'Enter' before you're
|
|
||||||
done.
|
|
||||||
|
|
||||||
I found myself wanting to space out my thoughts, create a code block as part of
|
|
||||||
a prompt, and generally have a scratch pad instead of just a text box. By
|
|
||||||
hitting `ctrl-g`, I can move the current prompt into my default editor (in my
|
|
||||||
case, `nvim`). From there I can continue to write, edit, and format with all the
|
|
||||||
affordances of an editor.
|
|
||||||
|
|
||||||
Once I'm done crafting the prompt, I can save (e.g. `:wq`) and Claude Code will
|
|
||||||
be primed with that text. I can then hit 'Enter' to let `claude` do its thing.
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
# Resume Specific Session
|
|
||||||
|
|
||||||
There are a few different ways to resume a [Claude
|
|
||||||
Code](https://code.claude.com/docs/en/overview) session.
|
|
||||||
|
|
||||||
First, if I have exited a session for the current project and I want to pick
|
|
||||||
back up with that most recent one, then I can use `claude --continue`.
|
|
||||||
|
|
||||||
If I have had a few recent sessions for the current project and I want to
|
|
||||||
remember what they were and pick up where I left off with one of them, then I
|
|
||||||
can use `claude --resume` (with no argument). That will open a picker where I
|
|
||||||
can browser through a summary of the recent sessions based on their starting
|
|
||||||
prompt. The one I pick is the session that will be resumed.
|
|
||||||
|
|
||||||
Finally, if I have grabbed a specific session ID (UUID) during the session from
|
|
||||||
the `/status` output, then I can reference that value directly.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ claude --resume 92170532-be31-4a91-b2a9-025b8fa78232
|
|
||||||
```
|
|
||||||
|
|
||||||
See `claude --help` for more details.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Stash The Current Prompt To Send Another First
|
|
||||||
|
|
||||||
I've been working my way through the current cohort of Matt Pocock's [Claude
|
|
||||||
Code for Real
|
|
||||||
Engineers](https://www.aihero.dev/cohorts/claude-code-for-real-engineers-2026-04).
|
|
||||||
The best part about going through a series of videos like this is being able to
|
|
||||||
pick up big and small tips and tricks from another person's workflow.
|
|
||||||
|
|
||||||
One of the small things I picked up in an early video is the ability to stash
|
|
||||||
the current prompt.
|
|
||||||
|
|
||||||
Let's say I've gone to the trouble of writing out a detailed prompt, `@`'ing
|
|
||||||
some files, and so forth. Then I realize I need first prompt Claude to do
|
|
||||||
something else first. Instead of copy-pasting that prompt into my notes,
|
|
||||||
deleting it, issuing a different prompt, and then pasting it back in, I can hit
|
|
||||||
`Ctrl-s`.
|
|
||||||
|
|
||||||
`Ctrl-s` will _stash_ the current prompt, clearing out the prompt input. I can
|
|
||||||
then type in something else. Once I hit enter for that new prompt, it will be
|
|
||||||
sent to Claude and the stashed prompt will be immediately populated back into
|
|
||||||
the input.
|
|
||||||
|
|
||||||
Though `Ctrl-s` is mentioned when you hit `?` from within `claude` session, I
|
|
||||||
don't see it documented anywhere in their [Interactive Mode
|
|
||||||
reference](https://code.claude.com/docs/en/interactive-mode).
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
# Allow Cursor To Be Launched From CLI
|
|
||||||
|
|
||||||
It is nice to be able to open Cursor for a specific project directly from the
|
|
||||||
terminal like so:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ cd ~/dev/my/project
|
|
||||||
|
|
||||||
$ cursor .
|
|
||||||
```
|
|
||||||
|
|
||||||
For the `cursor` launcher binary to be available like that, we have to find it
|
|
||||||
and add it to the path.
|
|
||||||
|
|
||||||
It is probably located in the `/Applications` folder and within that nested down
|
|
||||||
a couple directories is a `bin` directory that contains the binary we're looking
|
|
||||||
for.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ls /Applications/Cursor.app/Contents/Resources/app/bin
|
|
||||||
bin/
|
|
||||||
├── code*
|
|
||||||
├── cursor*
|
|
||||||
└── cursor-tunnel*
|
|
||||||
```
|
|
||||||
|
|
||||||
The `cursor` binary is what we want, so let's add that to our path. In my case,
|
|
||||||
I'll add this to my `~/.zshrc` file.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export PATH="/Applications/Cursor.app/Contents/Resources/app/bin:$PATH"
|
|
||||||
```
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# Cloudflare Allows CNAME For Apex Domain
|
|
||||||
|
|
||||||
If you want to set up a custom root (apex) domain with an app hosting provider
|
|
||||||
[like
|
|
||||||
Heroku](https://devcenter.heroku.com/articles/custom-domains#add-a-custom-root-domain),
|
|
||||||
you're going to need to work with a DNS provider that supports the non-standard
|
|
||||||
`ALIAS` records (or something equivalent).
|
|
||||||
|
|
||||||
In my case, I have my domain registered with Cloudflare. Cloudflare supports
|
|
||||||
this kind of CNAME lookup of an apex domain through [_CNAME
|
|
||||||
flattening_](https://developers.cloudflare.com/dns/cname-flattening/).
|
|
||||||
|
|
||||||
Unlike other registrars that use a separate `ALIAS` record concept, Cloudflare
|
|
||||||
allows you to set up a specialized `CNAME` record. Go into the DNS settings for
|
|
||||||
the domain of interest, click "Add Record", and then select `CNAME`. From there,
|
|
||||||
instead of entering a traditional subdomain like `www`, you put the `@` symbol
|
|
||||||
which tells Cloudflare that this is a record for the apex domain. That record
|
|
||||||
will still point to a target like `abc123.herokudns.com` as a more traditional
|
|
||||||
`CANME` would do.
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Set, Get, And Unset Env Vars With Dokku
|
|
||||||
|
|
||||||
The `dokku` CLI provides `config` subcommands for managing environment variables
|
|
||||||
for the target container.
|
|
||||||
|
|
||||||
An env var can be set for an active container with `config:set`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ dokku config:set app-name JEMALLOC_ENABLED=true MALLOC_CONF="stats_print:true"
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice I'm able to set multiple env vars at once if needed.
|
|
||||||
|
|
||||||
If I ever need to check what an env var is currently set to for one of my app
|
|
||||||
containers, I can use `config:get`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ dokku config:get app-name JEMALLOC_ENABLED
|
|
||||||
true
|
|
||||||
```
|
|
||||||
|
|
||||||
I can always override any value with another `config:set`. However, if I need to
|
|
||||||
entirely remove the env var, I can use `config:unset`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ dokku config:unset app-name MALLOC_CONF
|
|
||||||
```
|
|
||||||
|
|
||||||
[source](https://dokku.com/docs/configuration/environment-variables/)
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"excludes": ["README.md"],
|
|
||||||
"plugins": ["https://plugins.dprint.dev/markdown-0.16.0.wasm"]
|
|
||||||
}
|
|
||||||
@@ -9,10 +9,10 @@ test runs. Most of these files are tracked (already checked in to the
|
|||||||
repository). There are also many new files generated as part of the most recent
|
repository). There are also many new files generated as part of the most recent
|
||||||
test run.
|
test run.
|
||||||
|
|
||||||
I want to stage the changes to files that are already tracked, but hold off on
|
I want to staging the changes to files that are already tracked, but hold off
|
||||||
doing anything with the new files.
|
on doing anything with the new files.
|
||||||
|
|
||||||
Running `git add spec/cassettes` won't do the trick because that will pull in
|
Running `git add spec/cassettes` won't do the track because that will pull in
|
||||||
everything. Running `git add --patch spec/cassettes` will take long and be
|
everything. Running `git add --patch spec/cassettes` will take long and be
|
||||||
tedious. Instead what I want is the `-u` flag. It's short for _update_ which
|
tedious. Instead what I want is the `-u` flag. It's short for _update_ which
|
||||||
means it will only stage already tracked files.
|
means it will only stage already tracked files.
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
# Check If A File Has Changed In A Script
|
|
||||||
|
|
||||||
If I'm at the command line and I want to check if a file has changed, I can run
|
|
||||||
`git diff` and see what has changed. If I want to be more specific, I can run
|
|
||||||
`git diff README.md` to see if there are changes to that specific file.
|
|
||||||
|
|
||||||
If I'm trying to do this check in a script though, I want the command to clearly
|
|
||||||
tell the script _Yes_ or _No_. Usually a script looks for an exit code to
|
|
||||||
determine what path to take. But as long as `git diff` runs successfully,
|
|
||||||
regardless of whether or not their are changes, it is going to have an
|
|
||||||
affirmative exit code of `0`.
|
|
||||||
|
|
||||||
This is why `git diff` offers the `--exit-code` flag.
|
|
||||||
|
|
||||||
> Make the program exit with codes similar to diff(1). That is, it exits with 1
|
|
||||||
> if there were differences and 0 means no differences.
|
|
||||||
|
|
||||||
With that in mind, we can wire up a script with `git diff` that takes different
|
|
||||||
paths depending on whether or not there are changes.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
if ! git diff --exit-code README.md; then
|
|
||||||
echo "README.md has changes"
|
|
||||||
else
|
|
||||||
echo "README.md is clean"
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
We can take this a step further and instead use the `--quiet` flag.
|
|
||||||
|
|
||||||
> Disable all output of the program. Implies --exit-code. Disables execution of
|
|
||||||
> external diff helpers whose exit code is not trusted
|
|
||||||
|
|
||||||
This exhibits the same behavior as `--exit-code` and goes the additional step of
|
|
||||||
silencing diff output and disabling execution of external diff helpers like
|
|
||||||
`delta`.
|
|
||||||
|
|
||||||
See `man git-diff` for more details.
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
# Check If A File Is Under Version Control
|
|
||||||
|
|
||||||
The `git ls-files` command can be used with the `--error-unmatch` flag to check
|
|
||||||
if a file is under version control. It does this by checking if any of the
|
|
||||||
listed files appears on the _index_. If any does not, it is treated as an error.
|
|
||||||
|
|
||||||
In a project, I have a `README.md` that is under version control. And I have
|
|
||||||
`node_modules` that shouldn't be under version control (which is why they are
|
|
||||||
listed in my `.gitignore` file). I can check the README and a file somewhere in
|
|
||||||
`node_modules`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ git ls-files --error-unmatch README.md
|
|
||||||
README.md
|
|
||||||
|
|
||||||
❯ git ls-files --error-unmatch node_modules/@ai-sdk/anthropic/CHANGELOG.md
|
|
||||||
error: pathspec 'node_modules/@ai-sdk/anthropic/CHANGELOG.md' did not match any file(s) known to git
|
|
||||||
Did you forget to 'git add'?
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice the second command results in an error because of the untracked
|
|
||||||
`CHANGELOG.md` file in `node_modules`.
|
|
||||||
|
|
||||||
Here is another example of this at work while specifying multiple files:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ git ls-files --error-unmatch README.md node_modules/@ai-sdk/anthropic/CHANGELOG.md package.json
|
|
||||||
README.md
|
|
||||||
package.json
|
|
||||||
error: pathspec 'node_modules/@ai-sdk/anthropic/CHANGELOG.md' did not match any file(s) known to git
|
|
||||||
Did you forget to 'git add'?
|
|
||||||
```
|
|
||||||
|
|
||||||
Each tracked file gets listed and then the untracked file results in an error.
|
|
||||||
|
|
||||||
See `man git-ls-files` for more details.
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# Cherry Pick Multiple Commits At Once
|
|
||||||
|
|
||||||
I've always thought of `git cherry-pick` as being a command that you can run
|
|
||||||
against a single commit by specifying the SHA of that commit. That's how I've
|
|
||||||
always used it.
|
|
||||||
|
|
||||||
The man page for `git-cherry-pick` plainly states:
|
|
||||||
|
|
||||||
> Given one or more existing commits, apply the change each one introduces,
|
|
||||||
> recording a new commit for each.
|
|
||||||
|
|
||||||
We can cherry pick multiple commits at once in a single command. They will be
|
|
||||||
applied one at a time in the order listed.
|
|
||||||
|
|
||||||
Here we can see an example of applying two commits to the current branch and
|
|
||||||
the accompanying output as they are auto-merged.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git cherry-pick 5206af5 6362f41
|
|
||||||
Auto-merging test/services/event_test.rb
|
|
||||||
[jb/my-feature-branch 961f3deb] Use the other testing syntax
|
|
||||||
Date: Fri May 2 10:50:14 2025 -0500
|
|
||||||
1 file changed, 7 insertions(+), 7 deletions(-)
|
|
||||||
Auto-merging test/services/event_test.rb
|
|
||||||
[jb/my-feature-branch b15835d0] Make other changes to the test
|
|
||||||
Date: Fri May 2 10:54:48 2025 -0500
|
|
||||||
1 file changed, 7 insertions(+), 7 deletions(-)
|
|
||||||
```
|
|
||||||
|
|
||||||
If the commits cannot be cleanly merged, then you may need to do some manual
|
|
||||||
resolution as they are applied. Or maybe you want to try including the
|
|
||||||
`-Xpatience` merge strategy.
|
|
||||||
|
|
||||||
See `man git-cherry-pick` for more details. Make sure to look at the _Examples_
|
|
||||||
section which contains much more advanced examples beyond what is shown above.
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# Clear Entries From Git Stash
|
|
||||||
|
|
||||||
I often stash changes as I'm moving between branches, rebasing, or pulling in
|
|
||||||
changes from the remote. Usually these are changes that I will want to restore
|
|
||||||
with a `git stash pop` in a few moments.
|
|
||||||
|
|
||||||
However, sometimes these stashed changes are abandoned to time.
|
|
||||||
|
|
||||||
When I run `git stash list` on an active project, I see that there are nine
|
|
||||||
entries in the list. When I do `git show stash@{0}` and `git show stash@{1}` to
|
|
||||||
see the changes that comprise the latest two entries, I don't see anything I
|
|
||||||
care about.
|
|
||||||
|
|
||||||
I can get rid of those individual entries with, say, `git stash drop
|
|
||||||
stash@{0}`.
|
|
||||||
|
|
||||||
But I'm pretty confident that I don't care about any of the nine entries in my
|
|
||||||
stash list, so I want to _clear_ out all of them. I can do that with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git stash clear
|
|
||||||
```
|
|
||||||
|
|
||||||
Now when I run `git stash list`, I see nothing.
|
|
||||||
|
|
||||||
See `man git-stash` for more details.
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# Determine Absolute Path Of Top-Level Project Directory
|
|
||||||
|
|
||||||
The `git rev-parse` command is a git plumbing command for parsing different
|
|
||||||
kinds of things in git into a canonical form that can be used in a deterministic
|
|
||||||
way by scripts. I would typically think of using it to work with branch names,
|
|
||||||
tags, and other kinds of refs.
|
|
||||||
|
|
||||||
There is a handy, sorta off-label use for it in determining the absolute path of
|
|
||||||
the root directory for the current git repository. Use the `--show-toplevel`
|
|
||||||
flag with no other arguments.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ git rev-parse --show-toplevel
|
|
||||||
/Users/lastword/dev/jbranchaud/til
|
|
||||||
```
|
|
||||||
|
|
||||||
Here, I am in the local copy of [my TIL repo](https://github.com/jbranchaud/til). This command gives me the absolute
|
|
||||||
path of the top-level directory where that `.git` directory resides.
|
|
||||||
|
|
||||||
This is useful for scripts that need to orient themselves to the current
|
|
||||||
project's top-level directory regardless of what directory they are being
|
|
||||||
executed from. This is useful for things like a git hook script or monorepos
|
|
||||||
with scripts located in a specific sub-project directory.
|
|
||||||
|
|
||||||
Also worth mentioning is the `--show-superproject-working-tree` flag. In my TIL
|
|
||||||
repo, I have a private repository included as a submodule. Within that directory
|
|
||||||
`--show-toplevel` will produce the absolute path to the submodule. If I instead
|
|
||||||
want the absolute path of the _super project_ (in this case TIL), then I can use
|
|
||||||
this other flag.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ git rev-parse --show-toplevel
|
|
||||||
/Users/lastword/dev/jbranchaud/til/notes
|
|
||||||
|
|
||||||
❯ git rev-parse --show-superproject-working-tree
|
|
||||||
/Users/lastword/dev/jbranchaud/til
|
|
||||||
```
|
|
||||||
|
|
||||||
See `man git-rev-parse` for more details.
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
# Display All Git Log Entries In My Local Timezone
|
|
||||||
|
|
||||||
I tend to work with remote teams distributed across across multiple time zones.
|
|
||||||
In that context, it is important to have an awareness of what time zone each
|
|
||||||
person is operating in and to communicate clearly around that.
|
|
||||||
|
|
||||||
When looking at the output for `git log` on a distributed team, the timestamps
|
|
||||||
for each entry can be all over the place. If I want to understand when something
|
|
||||||
was committed, I have to look at the time as well as the time zone offset and
|
|
||||||
mentally translate it to my own time zone.
|
|
||||||
|
|
||||||
There is a `git config` option to alleviate this issue by having `git log`
|
|
||||||
convert and display all timestamps into your local time zone.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git config --global log.date rfc-local
|
|
||||||
```
|
|
||||||
|
|
||||||
Running that will add this entry to your _global_ git config file:
|
|
||||||
|
|
||||||
```
|
|
||||||
[log]
|
|
||||||
date = rfc-local
|
|
||||||
```
|
|
||||||
|
|
||||||
Now the time that was displaying as `Wed Apr 8 20:12:33 2026 -0400` will display
|
|
||||||
as `Wed, 8 Apr 2026 19:12:33 -0500`.
|
|
||||||
|
|
||||||
This also helps with smoothing out differences from DST and for commits produced
|
|
||||||
by AI agents in sandbox environments where the locale is set to UTC.
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
# Exclude A Directory During A Command
|
|
||||||
|
|
||||||
Many of the git commands we use, such as `git add`, `git restore`, etc., target
|
|
||||||
files and paths relative to the current directory. This is typically exactly
|
|
||||||
what we want, to stage and unstage and so forth the files and directories in
|
|
||||||
front of us.
|
|
||||||
|
|
||||||
I recently ran into a situation where I needed to restore a small subset of
|
|
||||||
changes. At the same time, I had a massive number of auto-generated files
|
|
||||||
recording HTTP interactions (hundreds of files, modified on the working tree).
|
|
||||||
I wanted to run a `git restore`, but wading through all those HTTP recording
|
|
||||||
files was not feasible.
|
|
||||||
|
|
||||||
I needed to exclude those files. They all belonged to a `spec/cassettes`
|
|
||||||
directory. I could exclude them with a _pathspec_ magic signature pattern which
|
|
||||||
is used to alter and limit the paths in a git command.
|
|
||||||
|
|
||||||
A _pathspec_ magic signature is a special pattern made up of a `:` followed by
|
|
||||||
some signature declaring what the pattern means.
|
|
||||||
|
|
||||||
The `(exclude)`, `!`, and `^` magic signatures all mean the same thing —
|
|
||||||
exclude. So, we can exclude a directory from a `git restore` command like so:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git restore --patch -- . ':!spec/cassettes'
|
|
||||||
```
|
|
||||||
|
|
||||||
We've employed two pathspec patterns here. The first, `.`, scopes everything to
|
|
||||||
the current directory. The second, `':!spec/cassettes'` excludes everything in
|
|
||||||
the `spec/cassettes` directory.
|
|
||||||
|
|
||||||
See `man gitglossary` for more on _pathspecs_.
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# Highlight Small Change On Single Line
|
|
||||||
|
|
||||||
Sometimes a change gets made on a single, long line of text in a Git tracked
|
|
||||||
file. If it is a small, subtle change, then it can be hard to pick out when
|
|
||||||
looking at the diff. A standard diff will show a green line of text stacked on
|
|
||||||
a red line of text with no more granular information.
|
|
||||||
|
|
||||||
There are two ways we can improve the diff output in these situations.
|
|
||||||
|
|
||||||
The first is built-in to git. It is the `--word-diff` flag which will visually
|
|
||||||
isolate the portions of the line that have changed.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git diff --word-diff README.md
|
|
||||||
```
|
|
||||||
|
|
||||||
Which will produce something like this:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
A collection of concise write-ups on small things I learn [-day to day-]{+day-to-day+} across a
|
|
||||||
```
|
|
||||||
|
|
||||||
The outgoing part is wrapped in `[-...-]` and the incoming part is wrapped in
|
|
||||||
`{+...+}`.
|
|
||||||
|
|
||||||
The second (and my preference) is to use
|
|
||||||
[`delta`](https://github.com/dandavison/delta) as an external differ and pager
|
|
||||||
for git.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git -c core.pager=delta diff README.md
|
|
||||||
```
|
|
||||||
|
|
||||||
I cannot visually demonstrate the difference in a standard code block. So I'll
|
|
||||||
describe it. We see a red and green line stacked on each other, but with muted
|
|
||||||
background colors. Then the specific characters that are different stand out
|
|
||||||
because they are highlighted with brighter red and green. I [posted a visual
|
|
||||||
here](https://bsky.app/profile/jbranchaud.bsky.social/post/3ln245orlxs2j).
|
|
||||||
|
|
||||||
`delta` can also be added as a standard part of your config like I demonstrate
|
|
||||||
in [Better Diffs With Delta](git/better-diffs-with-delta.md).
|
|
||||||
|
|
||||||
h/t to [Dillon Hafer's post on
|
|
||||||
`--word-diff`](https://til.hashrocket.com/posts/t994rwt3fg-finds-diffs-in-long-line)
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# List All Git Aliases From gitconfig
|
|
||||||
|
|
||||||
Running the `git config --list` command will show all of the configuration
|
|
||||||
settings you have for `git` relative to your current location. Though most of
|
|
||||||
these setting probably live in `~/.gitconfig`, you may also have some locally
|
|
||||||
specified ones in `.git/config`. This will grab them all including any `alias`
|
|
||||||
entries.
|
|
||||||
|
|
||||||
We can narrow things down to just `alias` entries using the `--get-regexp` flag.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git config --get-regexp '^alias\.'
|
|
||||||
|
|
||||||
alias.ap add --patch
|
|
||||||
alias.authors shortlog -s -n -e
|
|
||||||
alias.co checkout
|
|
||||||
alias.st status
|
|
||||||
alias.put push origin HEAD
|
|
||||||
alias.fixup commit --fixup
|
|
||||||
alias.squash commit --squash
|
|
||||||
alias.doff reset HEAD^
|
|
||||||
alias.add-untracked !git status --porcelain | awk '/\?\?/{ print $2 }' | xargs git add
|
|
||||||
alias.reset-authors commit --amend --reset-author -CHEAD
|
|
||||||
```
|
|
||||||
|
|
||||||
I use `git doff` all the time on feature branches to "pop" the latest commmit
|
|
||||||
onto the working copy. I was trying to remember exactly what the `git doff`
|
|
||||||
command is and this was an easy way to check.
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# Programmatically Grab SHA For Head Commit
|
|
||||||
|
|
||||||
When I use `gh browse path/to/some-file.txt`, it opens the browser to that file
|
|
||||||
in GitHub. However, it targets the default branch (`main`) by default which is
|
|
||||||
not very useful as a permalink because what that file looks like on `main` is
|
|
||||||
liable to change.
|
|
||||||
|
|
||||||
There is a `--commit` flag you can use to have it instead open to that file at a
|
|
||||||
specific commit SHA.
|
|
||||||
|
|
||||||
So what SHA do I pass as an argument to that flag?
|
|
||||||
|
|
||||||
Often what I would like to grab is a reference to the current version of the
|
|
||||||
file which is whatever it looks like for the `HEAD` commit. But `HEAD` is
|
|
||||||
another moving target reference. The `git rev-parse` command can translate
|
|
||||||
`HEAD` into a specific SHA though.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ git rev-parse --short HEAD
|
|
||||||
3402428
|
|
||||||
|
|
||||||
❯ git rev-parse HEAD
|
|
||||||
3402428aadc02cfdc9825c8feb593443e72f50cd
|
|
||||||
```
|
|
||||||
|
|
||||||
Either of those will work. I can use a bash command substitution then to tie it
|
|
||||||
all together into a single command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ gh browse path/to/some-file.txt --commit=$(git rev-parse --short HEAD)
|
|
||||||
```
|
|
||||||
|
|
||||||
See `man git-rev-parse` for more details.
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
While preparing some stats for a recent blog post on [A Decade of
|
While preparing some stats for a recent blog post on [A Decade of
|
||||||
TILs](https://www.visualmode.dev/a-decade-of-tils), I ran into an issue
|
TILs](https://www.visualmode.dev/a-decade-of-tils), I ran into an issue
|
||||||
referencing chunks of time further back than 2020.
|
referencing chuncks of time further back than 2020.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
❯ git diff --diff-filter=A --name-only HEAD@{2016-02-06}..HEAD@{2017-02-06} -- "*.md"
|
❯ git diff --diff-filter=A --name-only HEAD@{2016-02-06}..HEAD@{2017-02-06} -- "*.md"
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
# Restore File From One Branch To The Current
|
|
||||||
|
|
||||||
On one feature branch I have created some files and made changes to some
|
|
||||||
existing files as part of spiking a feature. Now I'm on a different branch
|
|
||||||
taking another shot at it. I want changes from one or two of the files. In the
|
|
||||||
past I've used `git-checkout` for this task. However, I believe this is one of
|
|
||||||
the use cases they had in mind when they added `git-restore`.
|
|
||||||
|
|
||||||
What I want to do is _restore_ the state of a file as it appears on some source
|
|
||||||
branch to my current branch. Here is what that looks like:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git restore --source=some-feature-branch app/models/contact.rb
|
|
||||||
```
|
|
||||||
|
|
||||||
Now when I check `git status` I'll see the state of that file on the
|
|
||||||
`some-feature-branch` branch overlayed on my current working copy. If the file
|
|
||||||
doesn't exist, it will be created.
|
|
||||||
|
|
||||||
See `man git-restore` for more details.
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
# Set Up GPG Signing Key
|
|
||||||
|
|
||||||
I wanted to have that "Verified" icon start showing up next to my commits in
|
|
||||||
GitHub. To do that, I need to generate a GPG key, configure the secret key in
|
|
||||||
GitHub, and then configure the public signing key with my git config.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# generate a gpg key
|
|
||||||
$ gpg --full-generate-key
|
|
||||||
|
|
||||||
# Pick the following options when prompted
|
|
||||||
# - Choose "RSA and RSA" (Options 1)
|
|
||||||
# - Max out key size at 4096
|
|
||||||
# - Choose expiration date (e.g. 0 for no expiration)
|
|
||||||
# - Enter "Real name" and "Email"
|
|
||||||
(I matched those to what is in my global git config)
|
|
||||||
# - Set passphrase (I had 1password generate a 4-word passphrase)
|
|
||||||
```
|
|
||||||
|
|
||||||
It may take a few seconds to create.
|
|
||||||
|
|
||||||
I can see it was created by listing my GPG keys.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gpg --list-secret-keys --keyid-format=long
|
|
||||||
[keyboxd]
|
|
||||||
---------
|
|
||||||
sec rsa4096/1A8656918A8D016B 2025-10-19 [SC]
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
I'll need the `1A8656918A8D016B` portion of that response for the next command
|
|
||||||
and it is what I set as my public signing key in my git config.
|
|
||||||
|
|
||||||
First, though, I add the full key block to my GitHub profile which I can copy
|
|
||||||
like so:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gpg --armor --export 1A8656918A8D016B | pbcopy
|
|
||||||
```
|
|
||||||
|
|
||||||
And then I paste that as a new GPG Key on GitHub under _Settings_ -> _SSH and
|
|
||||||
GPG Keys_.
|
|
||||||
|
|
||||||
Last, I update my global git config with the signing key and the preference to
|
|
||||||
sign commits:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git config --global user.signingkey 1A8656918A8D016B
|
|
||||||
git config --global commit.gpgsign true
|
|
||||||
```
|
|
||||||
|
|
||||||
Without `commit.gpgsign`, I would have to specify the `-S` flag every time I
|
|
||||||
want to create a signed commit.
|
|
||||||
|
|
||||||
[source](https://git-scm.com/book/ms/v2/Git-Tools-Signing-Your-Work)
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# Show Summary Stats For Current Branch
|
|
||||||
|
|
||||||
When I push a branch up to GitHub as a PR, there is a part of the UI that shows
|
|
||||||
you how many lines you've added and removed for this branch. It bases that off
|
|
||||||
the target branch which is typically your `main` branch.
|
|
||||||
|
|
||||||
The `git diff` command can provide those same stats right in the terminal. The
|
|
||||||
key is to specify the `--shortstat` flag which tells `git` to exclude other diff
|
|
||||||
output and only show:
|
|
||||||
|
|
||||||
- Number of files changed
|
|
||||||
- Number of insertions
|
|
||||||
- Number of deletions
|
|
||||||
|
|
||||||
Here is the summary stats for a branch I'm working on:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ git diff --shortstat main
|
|
||||||
8 files changed, 773 insertions(+), 25 deletions(-)
|
|
||||||
```
|
|
||||||
|
|
||||||
We have to be on our feature branch and then we point to the branch (or whatever
|
|
||||||
ref) we want to diff against. Since I want to know how my feature branch
|
|
||||||
compares to `main`, I specify that.
|
|
||||||
|
|
||||||
See `man git-diff` for more details.
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# Skip Git Hooks As Needed
|
|
||||||
|
|
||||||
Projects have Git hooks configured for all sorts of reasons. Most common are
|
|
||||||
`pre-commit` hooks which verify certain aspects of the contents of a commit.
|
|
||||||
A `pre-commit` hook could check that the tests all pass, that the changes don't
|
|
||||||
include any debugging statements, and so forth. There are all kinds of hooks
|
|
||||||
though, like `pre-rebase` and `post-checkout`.
|
|
||||||
|
|
||||||
These hooks can sometimes get in the way and we may need to skip or disable them
|
|
||||||
on a one-off basis.
|
|
||||||
|
|
||||||
Several Git commands offer a `--no-verify` flag which can skip running the hook
|
|
||||||
associated with that command.
|
|
||||||
|
|
||||||
- `git commit --no-verify` (skips `pre-commit` and `commit-msg` hooks)
|
|
||||||
- `git push --no-verify` (skips `pre-push` hook)
|
|
||||||
- `git merge --no-verify` (skips `pre-merge-commit` hook)
|
|
||||||
- `git am --no-verify` (skips `applypatch-msg` and `pre-applypatch` hooks)
|
|
||||||
|
|
||||||
If you look in the `.git/hooks` directory, there are several other hooks not
|
|
||||||
covered by the above. So, what if I am doing an action like `git checkout` and I
|
|
||||||
want to skip the `post-checkout` hook?
|
|
||||||
|
|
||||||
I can override the `hooksPath` config for that one command with the `-c` flag.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ git -c core.hooksPath=/dev/null checkout ...
|
|
||||||
```
|
|
||||||
|
|
||||||
By setting it to `/dev/null`, it will find *no* hooks available, so none will be
|
|
||||||
executed for this command.
|
|
||||||
|
|
||||||
See `man git-config` for more details on `core.hooksPath`.
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
# Undo Latest Changes Committed To Specific File
|
|
||||||
|
|
||||||
I'm reviewing the changes I've made in a PR before I request a review from my
|
|
||||||
team. There are a scattering of changes in one file that I've changed my mind
|
|
||||||
on. Everything else looks good though. So, I need to undo the changes in that
|
|
||||||
file before proceeding.
|
|
||||||
|
|
||||||
Manually undoing them is going to be clunky. There is a way to do it with `git
|
|
||||||
checkout`, but that is one of the ways in which `git-checkout` was overloaded
|
|
||||||
leading to the release of `git-restore`.
|
|
||||||
|
|
||||||
Let's use `git-restore` instead. By specifying a `--source`, I can tell `git`
|
|
||||||
what _ref_ in the commit history that file should be restored to. I'm on a
|
|
||||||
short-lived feature branch, so pointing to `main` is good enough.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git restore --source=main app/models/customer.rb
|
|
||||||
```
|
|
||||||
|
|
||||||
If I've changed a file at multiple points on this feature branch and I don't
|
|
||||||
want to undo all of them, then pointing to `main` is no longer going to work.
|
|
||||||
Instead, I can point to the commit right before the current one (`HEAD`) that
|
|
||||||
I'm trying to undo.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git restore --source=HEAD~ app/models/customer.rb
|
|
||||||
```
|
|
||||||
|
|
||||||
This really isn't much different than the `git-checkout` version, but I still
|
|
||||||
find it to be a little clearer.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git checkout HEAD~ -- app/models/customer.rb
|
|
||||||
```
|
|
||||||
|
|
||||||
See `man git-restore` for more details.
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
# Access Your GitHub Profile Photo
|
|
||||||
|
|
||||||
Let's say I have my [GitHub profile](https://github.com/jbranchaud) pulled up in
|
|
||||||
the browser.
|
|
||||||
|
|
||||||
```
|
|
||||||
https://github.com/jbranchaud
|
|
||||||
```
|
|
||||||
|
|
||||||
If I then add `.png` to the end of that in the URL bar:
|
|
||||||
|
|
||||||
```
|
|
||||||
https://github.com/jbranchaud.png
|
|
||||||
```
|
|
||||||
|
|
||||||
I'll be redirected to the URL where the full image file lives. In my case:
|
|
||||||
|
|
||||||
```
|
|
||||||
https://avatars.githubusercontent.com/u/694063?v=4
|
|
||||||
```
|
|
||||||
|
|
||||||
You can pull up yours `https://github.com/<username>.png` to access your profile
|
|
||||||
image.
|
|
||||||
|
|
||||||
[source](https://dev.to/10xlearner/how-to-get-the-profile-picture-of-a-github-account-1d82)
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# List PRs Awaiting Your Review
|
|
||||||
|
|
||||||
If you work on a software team or steward an open-source project, then there are
|
|
||||||
likely some open PRs that you've been tagged to review. I am usually able to
|
|
||||||
catch most review requests as they come up either from the GitHub email
|
|
||||||
notifications or by keeping an eye on the PRs tab of active projects. Sometimes
|
|
||||||
I get consumed by a task and something slips through the cracks.
|
|
||||||
|
|
||||||
There are a couple other ways to quickly check if anything is waiting on my
|
|
||||||
review.
|
|
||||||
|
|
||||||
From the web UI I can visit the following URL which will show all PRs across all
|
|
||||||
projects where my review has been requested:
|
|
||||||
|
|
||||||
[https://github.com/pulls/review-requested](https://github.com/pulls/review-requested)
|
|
||||||
|
|
||||||
The GitHub CLI (`gh`) can do the same and I can do it right from the terminal
|
|
||||||
instead of navigating several clicks within GitHub's web UI.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gh search prs --review-requested=@me --state=open
|
|
||||||
```
|
|
||||||
|
|
||||||
That too will list PRs across all projects that are open and awaiting my review.
|
|
||||||
|
|
||||||
If that one ends up being a little too noisy, you can also use `gh` to _list_
|
|
||||||
just PRs for the current project:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gh pr list --search "review-requested:@me"
|
|
||||||
```
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
# Open A PR To An Unforked Repo
|
|
||||||
|
|
||||||
Sometimes I will clone a repo to explore the source code or to look into a
|
|
||||||
potential bug. If my curiosity takes me far enough to make some changes, then I
|
|
||||||
jump through the hoops of creating a fork, reconfiguring branches, pushing to my
|
|
||||||
fork, and then opening the branch as a PR against the original repo.
|
|
||||||
|
|
||||||
The `gh` CLI allows me to avoid all that hoop-jumping. Directly from the cloned
|
|
||||||
repo I can use `gh` to create a new PR. It will prompt me to creat a fork. If I
|
|
||||||
accept, it will seamlessly create it and then open a PR from my fork to the
|
|
||||||
original.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gh pr create
|
|
||||||
```
|
|
||||||
|
|
||||||
This allows me to create the PR with a few prompts from the CLI. If you prefer,
|
|
||||||
you can include the `--web` flag to open the PR creation screen directly in the
|
|
||||||
browser.
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
# Open File To Specific Line In Browser
|
|
||||||
|
|
||||||
Often one of the best ways to point a teammate to a line of code is to share a
|
|
||||||
GitHub link to a specific file and line number. Sometimes even a specific
|
|
||||||
commit.
|
|
||||||
|
|
||||||
For the longest time I would manually open GitHub, navigate to that file, and so
|
|
||||||
forth. The `gh` CLI supports this with the `browse` subcommand and it takes way
|
|
||||||
less time if you already have the repo in your local filesystem.
|
|
||||||
|
|
||||||
For instance, if I want to point you to line 11 of the `zshrc.local` file in my
|
|
||||||
`dotfiles` repo, I can run the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gh browse zshrc.local:11
|
|
||||||
```
|
|
||||||
|
|
||||||
That would open a browser tab to
|
|
||||||
[https://github.com/jbranchaud/dotfiles/blob/main/zshrc.local?plain=1#L11](https://github.com/jbranchaud/dotfiles/blob/main/zshrc.local?plain=1#L11).
|
|
||||||
|
|
||||||
If I wanted a range of lines, I could change it from `11` to, say, `11-27`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gh browse zshrc.local:11-27
|
|
||||||
```
|
|
||||||
|
|
||||||
And I would see this in the browser --
|
|
||||||
[https://github.com/jbranchaud/dotfiles/blob/main/zshrc.local?plain=1#L11-L27](https://github.com/jbranchaud/dotfiles/blob/main/zshrc.local?plain=1#L11-L27).
|
|
||||||
|
|
||||||
Both of these URLs are pointing to the `main` branch. If I instead want to
|
|
||||||
reference a specific commit, I can use the `--commit` flag.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gh browse zshrc.local:11-27 --commit=f2f9e78d4fc784643f725c88f7a5a7a077e7f261
|
|
||||||
```
|
|
||||||
|
|
||||||
I grabbed that from the latest commit in `git log`. That opens to
|
|
||||||
[https://github.com/jbranchaud/dotfiles/blob/f2f9e78d4fc784643f725c88f7a5a7a077e7f261/zshrc.local?plain=1#L11-L27](https://github.com/jbranchaud/dotfiles/blob/f2f9e78d4fc784643f725c88f7a5a7a077e7f261/zshrc.local?plain=1#L11-L27).
|
|
||||||
|
|
||||||
Another way of doing that would be to use `git rev-parse HEAD`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gh browse zshrc.local:11-27 --commit=$(git rev-parse HEAD)
|
|
||||||
```
|
|
||||||
|
|
||||||
See `gh browse --help` for more details.
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
# Process JSON Output From gh With jq
|
|
||||||
|
|
||||||
The `gh` (GitHub) CLI is useful for accessing data about your profile and
|
|
||||||
projects from the terminal. With the `--json` flag, we can access the data in a
|
|
||||||
structured way which is useful for scripting.
|
|
||||||
|
|
||||||
Here is an example of pulling a list of all my repositories, limiting each
|
|
||||||
entity to just the `nameWithOwner` and `description`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ gh repo list --limit 1000 --json nameWithOwner,description
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"description": "My personal site -- joshbranchaud.com",
|
|
||||||
"nameWithOwner": "jbranchaud/personal-site"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Private repo for the NOTES.md of my TIL repo",
|
|
||||||
"nameWithOwner": "jbranchaud/til-notes-private"
|
|
||||||
},
|
|
||||||
...
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
If I'm using the `--json` flag, then I can add in the `--jq` flag to apply a
|
|
||||||
`jq` query for additional processing of the output.
|
|
||||||
|
|
||||||
Here I convert it to a series of tuples:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ gh repo list --limit 1000 --json nameWithOwner,description \
|
|
||||||
--jq '.[] | [.nameWithOwner, .description]'
|
|
||||||
[
|
|
||||||
"jbranchaud/personal-site",
|
|
||||||
"My personal site -- joshbranchaud.com"
|
|
||||||
]
|
|
||||||
[
|
|
||||||
"jbranchaud/til-notes-private",
|
|
||||||
"Private repo for the NOTES.md of my TIL repo"
|
|
||||||
]
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
Then I can add one more pipe to that `jq` query to turn it into _tab-separated
|
|
||||||
values_ using
|
|
||||||
[`@tsv`](https://jqlang.org/manual/v1.5/#format-strings-and-escaping):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ gh repo list --limit 1000 --json nameWithOwner,description \
|
|
||||||
--jq '.[] | [.nameWithOwner, .description] | @tsv'
|
|
||||||
jbranchaud/personal-site My personal site -- joshbranchaud.com
|
|
||||||
jbranchaud/til-notes-private Private repo for the NOTES.md of my TIL repo
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
This is useful because I can then pipe it to another program, such as an `fzf`
|
|
||||||
command like [this repo selector that opens the selected one in the
|
|
||||||
browser](https://github.com/jbranchaud/dotfiles/commit/f964ca10c6c4db3475411c2991dc2f1dfd18c818).
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# Target Another Repo When Creating A PR
|
|
||||||
|
|
||||||
I have a [`dotfiles` repo](https://github.com/jbranchaud/dotfiles) that I forked
|
|
||||||
from [`dkarter/dotfiles`](https://github.com/dkarter/dotfiles). I'm adding a
|
|
||||||
bunch of my own customizations on a `main` branch while continually pulling in
|
|
||||||
and merging upstream changes.
|
|
||||||
|
|
||||||
The primary remote according to `gh` is `jbranchaud/dotfiles`. 98% of the time
|
|
||||||
that is what I want. However, I occasionally want to share some changes upstream
|
|
||||||
via a PR. Running `gh pr create` as is will create a PR against my fork. To
|
|
||||||
override this on a one-off basis, I can use the `--repo` flag.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gh pr create --repo dkarter/dotfiles
|
|
||||||
```
|
|
||||||
|
|
||||||
This will create a PR against `dkarter:master` from my branch (e.g.
|
|
||||||
[`jbranchaud:jb/fix-hardcoded-paths`](https://github.com/dkarter/dotfiles/pull/373)).
|
|
||||||
|
|
||||||
See `man gh-pr-create` for more details.
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# Tell gh What The Default Repo Is
|
|
||||||
|
|
||||||
I recently forked [dkarter/dotfiles](https://github.com/dkarter/dotfiles) as a
|
|
||||||
way of bootstrapping a robust dotfile config for a new machine that I could
|
|
||||||
start making customizations to. I'm maintaining a `my-dotfiles` branch and keep
|
|
||||||
things in sync with the original upstream repo.
|
|
||||||
|
|
||||||
When trying to go to *my* fork of the repo
|
|
||||||
([jbranchaud/dotfiles](https://github.com/jbranchaud/dotfiles)) in the web with
|
|
||||||
the `gh` CLI tool, I ran into a weird issue. It was instead opening up to
|
|
||||||
`dkarter/dotfiles`.
|
|
||||||
|
|
||||||
`gh` was under the wrong impression which repo should be considered the default.
|
|
||||||
To clarify things for `gh`, there is a command to set the default repo.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ gh repo set-default jbranchaud/dotfiles
|
|
||||||
✓ Set jbranchaud/dotfiles as the default repository for the current directory
|
|
||||||
```
|
|
||||||
|
|
||||||
Now when I run `gh repo view --web`, it opens the browser to my fork of the
|
|
||||||
dotfiles.
|
|
||||||
|
|
||||||
But where does this setting live?
|
|
||||||
|
|
||||||
Opening this repo's `.git/config` file I can see a section for the `origin`
|
|
||||||
remote that includes a new line for `gh-resolved`. This being set to `base`
|
|
||||||
tells `gh` that this remote is the one to treat as the default repo.
|
|
||||||
|
|
||||||
```
|
|
||||||
[remote "origin"]
|
|
||||||
url = git@github.com:jbranchaud/dotfiles.git
|
|
||||||
fetch = +refs/heads/*:refs/remotes/origin/*
|
|
||||||
gh-resolved = base
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
See `gh repo set-default --help` for more details.
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
# Check Ruby Version For Production App
|
|
||||||
|
|
||||||
While deploying a fresh Rails app to Heroku recently, I ran into an issue. The
|
|
||||||
`it` block argument wasn't working despite being on Ruby 4.0. Or so I thought.
|
|
||||||
|
|
||||||
Running the following command reported the Ruby version of that Heroku server
|
|
||||||
instance:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ heroku run -- ruby --version
|
|
||||||
Running ruby --version on ⬢ my-app... up, run.3090
|
|
||||||
ruby 3.3.9 (2025-07-24 revision f5c772fc7c) [x86_64-linux]
|
|
||||||
```
|
|
||||||
|
|
||||||
I was on `3.3.9` which must have been the fallback default at the time.
|
|
||||||
|
|
||||||
Though I had set the Ruby version in my `.ruby-version` file, I had neglected to
|
|
||||||
specify it in the `Gemfile` as well. Once I added it to the `Gemfile` and
|
|
||||||
redeployed, my Heroku server instance was running the expected version of Ruby.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ heroku run -- ruby --version
|
|
||||||
Running ruby --version on ⬢ my-app... up, run.5353
|
|
||||||
ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [x86_64-linux]
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: because [I have set `HEROKU_ORGANIZATION` and
|
|
||||||
`HEROKU_APP`](set-default-team-and-app-for-project.md) in my environment
|
|
||||||
(`.envrc`) for the local copy of the app, I don't need to specify those when
|
|
||||||
running the `heroku run` command above.
|
|
||||||
|
|
||||||
See `heroku run --help` for more details.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Specify Default Team And App For Project
|
|
||||||
|
|
||||||
Typically when you run commands with the Heroku CLI you'll need to specify the
|
|
||||||
name of the app on Heroku you're targeting with the `--app` flag. However, to
|
|
||||||
first see the names of the apps you may want to run `heroku apps` (or `heroku
|
|
||||||
list`). That will list the apps for your default team.
|
|
||||||
|
|
||||||
If you need to see apps for a different team (i.e. organization), you'll need to
|
|
||||||
specify that team either with the `--team` flag or by setting that as an
|
|
||||||
environment variable.
|
|
||||||
|
|
||||||
Here I do the latter in an `.envrc` file:
|
|
||||||
|
|
||||||
```
|
|
||||||
# Heroku
|
|
||||||
export HEROKU_ORGANIZATION=visualmode
|
|
||||||
```
|
|
||||||
|
|
||||||
Once that is set and the environment reloaded, running `heroku apps` will show
|
|
||||||
the apps specific to that team on Heroku.
|
|
||||||
|
|
||||||
Similarly, if you want to set a default app for your project so that you don't
|
|
||||||
have to always specify the `--app` flag, you can update your `.envrc`
|
|
||||||
accordingly.
|
|
||||||
|
|
||||||
```
|
|
||||||
# Heroku
|
|
||||||
export HEROKU_ORGANIZATION=visualmode
|
|
||||||
export HEROKU_APP=my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
I had a hard time finding official documentation for this which is why I'm
|
|
||||||
writing this up here. I've manually verified this works with my own team and
|
|
||||||
app.
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# Allow Number Input To Accept Decimal Values
|
|
||||||
|
|
||||||
Here is a number input element:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<input type="number" id="amount" required class="border" />
|
|
||||||
```
|
|
||||||
|
|
||||||
This renders an empty number input box with up and down arrows which will, by
|
|
||||||
default, increment or decrement the value by **1**.
|
|
||||||
|
|
||||||
Of course, I can manually edit the input typing in a value like `1.25`.
|
|
||||||
|
|
||||||
However, when I submit that via an HTML form, the submission will be prevented
|
|
||||||
and the browser will display a validation error.
|
|
||||||
|
|
||||||
> Please enter a valid value. The two nearest valid values are 1 and 2.
|
|
||||||
|
|
||||||
If I want to be able to input a decimal value like this, I need to change the
|
|
||||||
`step` value. It defaults to `1`, but I could change it to `2`, `10`, or in
|
|
||||||
this case to `0.01`.
|
|
||||||
|
|
||||||
```html
|
|
||||||
<input type="number" step="0.01" id="amount" required class="border" />
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice now that as you click the up and down arrows, the value is incremented
|
|
||||||
and decremented by **0.01** at a time.
|
|
||||||
|
|
||||||
If I want to maintain the step value of `1` while allowing decimal values, I
|
|
||||||
can instead set the `step` value to be `any`.
|
|
||||||
|
|
||||||
```html
|
|
||||||
<input type="number" step="any" id="amount" required class="border" />
|
|
||||||
```
|
|
||||||
|
|
||||||
See the [MDN docs on number
|
|
||||||
inputs](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/number)
|
|
||||||
for more details.
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Hide Overflowing Text For Google Sheets Column
|
|
||||||
|
|
||||||
I imported a big CSV into a new Google Sheets document. This included a
|
|
||||||
"Description" column with many of the descriptions varying between 50 and 80
|
|
||||||
characters. The bottom line is that the description column was flowing over the
|
|
||||||
top of the columns next to it. Instead of expanding the width of that column as
|
|
||||||
far as the largest description, I wanted to hide the _overflow_.
|
|
||||||
|
|
||||||
The way to do this in Google Sheets is to highlight the entire column by
|
|
||||||
clicking on the column grouping. Then under the _Format_ menu item is a
|
|
||||||
_Wrapping_ submenu. The _Clip_ option is what I was looking for because it clips
|
|
||||||
the text that gets shown at the edge of the column.
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Format A List Of Items By Locale
|
|
||||||
|
|
||||||
The `Intl` module includes a [`ListFormat`
|
|
||||||
object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/ListFormat)
|
|
||||||
which can be used to format a list of items in a consistent way across locales.
|
|
||||||
|
|
||||||
I've reinvented the wheel of writing a helper function numerous times across
|
|
||||||
projects for formatting a list of items that accounts for formatting based on
|
|
||||||
how many items there are. This built-in function handles that with the added
|
|
||||||
benefit of working across locales.
|
|
||||||
|
|
||||||
Here are lists of three, two, and one items formatted in the `long` styles for
|
|
||||||
US english.
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
> const formatter = new Intl.ListFormat('en', { style: 'long', type: 'conjunction' });
|
|
||||||
undefined
|
|
||||||
|
|
||||||
> formatter.format(['Alice', 'Bob', 'Carla'])
|
|
||||||
'Alice, Bob, and Carla'
|
|
||||||
|
|
||||||
> formatter.format(['Coffee', 'Tea'])
|
|
||||||
'Coffee and Tea'
|
|
||||||
|
|
||||||
> formatter.format(['Taco'])
|
|
||||||
'Taco'
|
|
||||||
```
|
|
||||||
|
|
||||||
The difference between `long` and `short` style for a `conjunction` is _and_
|
|
||||||
versus _&_. In addition to the type`conjunction`, you could also use
|
|
||||||
`disjunction` which will do an _or_ instead of an _and_. I'm not sure what
|
|
||||||
you'd use the `unit` type for.
|
|
||||||
|
|
||||||
You could use another locale, such as French, as well:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
> const formatter = new Intl.ListFormat('fr', { style: 'long', type: 'conjunction' });
|
|
||||||
undefined
|
|
||||||
|
|
||||||
> formatter.format(['café', 'thé'])
|
|
||||||
'café et thé'
|
|
||||||
```
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Get User's Preferred Language From Browser
|
|
||||||
|
|
||||||
A great way to determine a user's preferred language if you aren't able to ask
|
|
||||||
them directly is to look at the language setting for their browser's UI.
|
|
||||||
|
|
||||||
We can get this from the instance of
|
|
||||||
[`Navigator`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator) in the
|
|
||||||
user's JavaScript runtime within the browser.
|
|
||||||
|
|
||||||
My browser's UI is set to US English, which I can verify like so:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
> navigator.language
|
|
||||||
'en-US'
|
|
||||||
```
|
|
||||||
|
|
||||||
This is useful for all sorts of things like [formatting dates for
|
|
||||||
display](basic-date-formatting-without-a-library.md):
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
> const now = new Date();
|
|
||||||
> Intl.DateTimeFormat(navigator.language).format(now)
|
|
||||||
'5/14/2026'
|
|
||||||
```
|
|
||||||
|
|
||||||
Or for [formatting other kinds of units for
|
|
||||||
display](formatting-values-with-units-for-display.md):
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
> const milesFormat =
|
|
||||||
Intl.NumberFormat(navigator.language, { style: "unit", unit: "mile" });
|
|
||||||
> milesFormat.format(1500)
|
|
||||||
"1,500 mi"
|
|
||||||
```
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
# Describe Current Changes And Create New Change
|
|
||||||
|
|
||||||
One of the first patterns I learned with `jj` was a pair of commands to
|
|
||||||
essentially "commit" the working copy and start a fresh, new change. So if I am
|
|
||||||
done making some changes, I can add a description to the `(no description)`
|
|
||||||
working copy and then start a new working copy _change_.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ jj describe -m "Add status subcommand to show current status"
|
|
||||||
$ jj new
|
|
||||||
```
|
|
||||||
|
|
||||||
I learned from [Steve](https://steveklabnik.com/) in the [jj
|
|
||||||
discord](https://discord.gg/dkmfj3aGQN) that a shorthand for this pattern is to
|
|
||||||
use the `jj commit` command directly.
|
|
||||||
|
|
||||||
> When called without path arguments or `--interactive`, `jj commit` is
|
|
||||||
> equivalent to `jj describe` followed by `jj new`.
|
|
||||||
|
|
||||||
That means, instead of the above pair of commands, I could have done:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ jj commit -m "Add status subcommand to show current status"
|
|
||||||
```
|
|
||||||
|
|
||||||
That would have had the same result in my case. However, notice the caveats
|
|
||||||
mentioned in the quote above and check out `man jj-commit` for more details on
|
|
||||||
that.
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# Squash Changes Into Parent Commit Interactively
|
|
||||||
|
|
||||||
While I have some changes in progress as part of the working copy, I can squash
|
|
||||||
them into the previous / parent commit with the `jj squash` command. Running
|
|
||||||
that command as is will apply all the working copy changes to the parent leaving
|
|
||||||
the current revision empty.
|
|
||||||
|
|
||||||
I can also interactively squash those changes similar in spirit to how I might
|
|
||||||
use `git add --patch` to stage and then amend specific changes into the previous
|
|
||||||
commit with `git`. This can be done with [`jj`](https://github.com/jj-vcs/jj)
|
|
||||||
using `squash` with the `-i` flag.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
jj squash -i # or --interactive
|
|
||||||
```
|
|
||||||
|
|
||||||
This will open up a TUI where I can click around or use keys. Each file in the
|
|
||||||
source revision (in my case, the working copy) will be listed. I can move the
|
|
||||||
cursor between them hitting _space_ to toggle them in or out of the squash
|
|
||||||
selection.
|
|
||||||
|
|
||||||
I can also hit `f` over a given file to toggle _folding_. When folding is on, a
|
|
||||||
diff of the file will be disclosed with checkboxes for toggling individual
|
|
||||||
hunks and lines.
|
|
||||||
|
|
||||||
Once I'm satisfied with my interactive selection, I can hit `c` to confirm and
|
|
||||||
only the selected files and changes will be squashed into the parent.
|
|
||||||
|
|
||||||
See `man jj-squash` for more details.
|
|
||||||
|
|
||||||
[source](https://steveklabnik.github.io/jujutsu-tutorial/real-world-workflows/the-squash-workflow.html)
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
# Count Number Of Tokens In A File
|
|
||||||
|
|
||||||
Over time you have accumulated a bunch of small directives, corrections, and
|
|
||||||
project details in your `CLAUDE.md` or `AGENTS.md` file. The file doesn't seem
|
|
||||||
too big, but you are mindful that it is being included in every prompt. How many
|
|
||||||
tokens is it eating from the context window?
|
|
||||||
|
|
||||||
OpenAI's BPE (Byte Pair Encoding) tokenization library,
|
|
||||||
[`tiktoken`](https://github.com/openai/tiktoken), is an open-source Python
|
|
||||||
package. If it is installed on our machine, then we can use it as part of the
|
|
||||||
following one-liner to check a file:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ python -c "import tiktoken, sys; print(len(tiktoken.encoding_for_model('gpt-4o').encode(open(sys.argv[1], 'r', encoding='utf-8').read())))" \
|
|
||||||
AGENTS.md
|
|
||||||
1018
|
|
||||||
```
|
|
||||||
|
|
||||||
I ran this against the `AGENTS.md` file in a team project I'm on. It came out to
|
|
||||||
1018 tokens. This is a very good approximation based on the tokenizer trained
|
|
||||||
for `gpt-4o`. The tokenizers may vary a little from model to model, but the
|
|
||||||
differences for our purposes here are going to be negligible.
|
|
||||||
|
|
||||||
This one-liner gets the "first" argument to the command, reads it in, and runs
|
|
||||||
that string against the tokenizer. The length of the tokenized encoding is then
|
|
||||||
printed.
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
# Add A Bunch Of CLI Utilities With coreutils
|
|
||||||
|
|
||||||
The [`coreutils`](https://www.gnu.org/software/coreutils/) project is a
|
|
||||||
collection of useful utilities that every operating system ought to have.
|
|
||||||
|
|
||||||
> The GNU Core Utilities are the basic file, shell and text manipulation
|
|
||||||
> utilities of the GNU operating system. These are the core utilities which are
|
|
||||||
> expected to exist on every operating system.
|
|
||||||
|
|
||||||
While many of these utilities are redundant with BSD utilities that MacOS
|
|
||||||
chooses to ship with, there are some differences in the overlapping ons and then
|
|
||||||
many additions from `coreutils`.
|
|
||||||
|
|
||||||
They can be installed with Homebrew:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ brew install coreutils
|
|
||||||
```
|
|
||||||
|
|
||||||
And then you should have some new things available on your path. Take `shuf`, for
|
|
||||||
instance. This utility can shuffle and select items from a file or incoming
|
|
||||||
lines from another command. Here I use it to randomly grab a number between 1
|
|
||||||
and 5 (with the help of `seq`):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ seq 1 5 | shuf -n 1
|
|
||||||
3
|
|
||||||
|
|
||||||
❯ seq 1 5 | shuf -n 1
|
|
||||||
2
|
|
||||||
|
|
||||||
❯ seq 1 5 | shuf -n 1
|
|
||||||
5
|
|
||||||
```
|
|
||||||
|
|
||||||
Or how about some utilities for manipulating file names? Among others there is
|
|
||||||
`realpath`, `basename`, and `dirname`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ realpath README.md
|
|
||||||
/Users/lastword/dev/jbranchaud/til/README.md
|
|
||||||
|
|
||||||
❯ realpath README.md | xargs basename
|
|
||||||
README.md
|
|
||||||
|
|
||||||
❯ realpath README.md | xargs dirname
|
|
||||||
/Users/lastword/dev/jbranchaud/til
|
|
||||||
```
|
|
||||||
|
|
||||||
See the [manual](https://www.gnu.org/software/coreutils/manual/coreutils.html)
|
|
||||||
for many more details.
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Capture Screenshoot To Clipboard From CLI
|
|
||||||
|
|
||||||
MacOS comes with a `screencapture` utility that you can run from the terminal
|
|
||||||
to activate the built-in screenshot functionality on Mac.
|
|
||||||
|
|
||||||
Usually when I am taking a screenshot, I want to do something with it right
|
|
||||||
away. Such as paste it into an application or group chat. The `-c` flag forces
|
|
||||||
the screen capture to go the clipboard.
|
|
||||||
|
|
||||||
I also generally want to capture a specific area of the screen so that the
|
|
||||||
captured image includes the right amount of context and nothing more. The `-i`
|
|
||||||
flag puts you in interactive screen capture mode. That means your cursor will
|
|
||||||
turn into a crosshair that you can use to make a drag selection of the capture
|
|
||||||
area.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ screencapture -ic
|
|
||||||
```
|
|
||||||
|
|
||||||
Select an area to capture, it's now on your clipboard, paste it where you need
|
|
||||||
it.
|
|
||||||
|
|
||||||
Note: The first time you run this command, your terminal program (e.g. iTerm2)
|
|
||||||
may prompt you for the necessary OS permissions in order to capture images of
|
|
||||||
your screen. You'll need to grant those permissions and then rerun the command.
|
|
||||||
|
|
||||||
See `man screencapture` for more details.
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
# Clean Up Item Layout In Finder Window
|
|
||||||
|
|
||||||
Sometimes while doing a bunch of manual drag-n-drop of files and folders in a
|
|
||||||
Finder.app window, I'll end up with a visual mess. Compared to other folders,
|
|
||||||
nothing is organized on the grid.
|
|
||||||
|
|
||||||
I can tell Finder.app to clean that up with the _Clean Up_ menu option.
|
|
||||||
|
|
||||||
While focused on the folder that I'm concerned about, I can go to _View_ >
|
|
||||||
_Clean Up_ in the top menu. Everything will snap into place.
|
|
||||||
|
|
||||||
On the specific Finder.app window, there is also a triple-dot actions menu that
|
|
||||||
appears on the top right. The _Clean Up_ action is available there as well.
|
|
||||||
|
|
||||||
There is also a _Clean Up By_ option which is a nice way to organize by some
|
|
||||||
attribute, such as the type (e.g Folder/File and extension).
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
# Control Which Monitor App Switcher Appears On
|
|
||||||
|
|
||||||
For the most part when I hit `cmd+tab` (and `cmd+shift+tab`) to switch between
|
|
||||||
apps, the visual switcher UI (which shows a row of the open apps) appears on my
|
|
||||||
main monitor. However, sometimes I will be hitting `cmd+tab` and nothing shows
|
|
||||||
up on my main monitor. I look to the right at my side monitor and there is the
|
|
||||||
app switcher UI.
|
|
||||||
|
|
||||||
Why is it appearing over there all of a sudden?
|
|
||||||
|
|
||||||
The reason is that the app switcher UI is anchored to the same screen where the
|
|
||||||
doc is located. Though the doc defaults to my main monitor, if I access the doc
|
|
||||||
from the side monitor, now it is anchored there.
|
|
||||||
|
|
||||||
To switch it back, I just have to make the doc slide up on my main monitor by
|
|
||||||
running my mouse down to the bottom of that screen.
|
|
||||||
|
|
||||||
The switch up was because I accidentally accessed the doc on my side monitor
|
|
||||||
without realizing.
|
|
||||||
|
|
||||||
[source](https://superuser.com/a/744680)
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# Detect How Long A User Has Been Idle
|
|
||||||
|
|
||||||
The `ioreg` utility on MacOS dumps the I/O Kit registry tree. This lets us look
|
|
||||||
at the state of all hardware devices and drivers registered with I/O Kit.
|
|
||||||
Looking specifically at the Human Interface Device subsystem (`IOHIDSystem`), we
|
|
||||||
can find a handful of properties including the `HIDIdleTime`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ ioreg -c IOHIDSystem | awk '/HIDIdleTime/'
|
|
||||||
| | | "HIDIdleTime" = 91831000
|
|
||||||
```
|
|
||||||
|
|
||||||
That value is the number of nanoseconds since a human input device was last
|
|
||||||
interacted with. That is the amount of time the user (me) has been idle.
|
|
||||||
|
|
||||||
I can convert this to seconds, which is the small amount of time between me
|
|
||||||
hitting enter in the terminal and the command finding the idle time.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ ioreg -c IOHIDSystem | awk '/HIDIdleTime/ {printf "%.2f seconds\n", $NF/1000000000}'
|
|
||||||
0.13 seconds
|
|
||||||
```
|
|
||||||
|
|
||||||
I can run this in `watch` to see the elapsed idle time increment.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
watch -n 1 "echo -n 'Idle time: '; ioreg -c IOHIDSystem | awk '/HIDIdleTime/ {printf \"%.1f seconds\\n\", \$NF/1000000000}'"
|
|
||||||
```
|
|
||||||
|
|
||||||
After watching the _idle time_ increment for a bit, I can move the mouse and
|
|
||||||
watch it reset on the next `watch` loop.
|
|
||||||
|
|
||||||
This could be used as part of a script that takes certain actions after the user
|
|
||||||
has been idle for a while, like putting the display to sleep or stopping a time
|
|
||||||
tracker app.
|
|
||||||
|
|
||||||
There is a _lot_ going on in the `ioreg` output and it's hard to make sense of
|
|
||||||
hardly any of it. I found running `ioreg -c IOHIDSystem | less`, searching for
|
|
||||||
`IdleTime`, and browsing from there to be a good starting point.
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
# Inspect Assertions Preventing Sleep
|
|
||||||
|
|
||||||
The `pmset` command is for inspecting and manipulating _Power Management
|
|
||||||
Settings_ on MacOS. The `-g` flag is for _getting_ details. We can get a summary
|
|
||||||
of power assertions with `-g assertions`. These assertions are ways that the
|
|
||||||
system and display are prevented from sleeping.
|
|
||||||
|
|
||||||
A common assertion preventing sleep is the user being active. Another example of
|
|
||||||
an assertion is a program like `caffeinate` that sets a timeout preventing sleep
|
|
||||||
for a fixed period of time.
|
|
||||||
|
|
||||||
Here I activate a 30 minute (1600 second) `caffeinate` session and then I
|
|
||||||
inspect the power management assertions which shows the details of that
|
|
||||||
assertion as well as two others.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ caffeinate -t 1600 &
|
|
||||||
[1] 98217
|
|
||||||
|
|
||||||
❯ pmset -g assertions
|
|
||||||
2025-11-02 13:20:57 -0600
|
|
||||||
Assertion status system-wide:
|
|
||||||
BackgroundTask 0
|
|
||||||
ApplePushServiceTask 0
|
|
||||||
UserIsActive 1
|
|
||||||
PreventUserIdleDisplaySleep 0
|
|
||||||
PreventSystemSleep 0
|
|
||||||
ExternalMedia 0
|
|
||||||
PreventUserIdleSystemSleep 1
|
|
||||||
NetworkClientActive 0
|
|
||||||
Listed by owning process:
|
|
||||||
pid 98217(caffeinate): [0x00045477000194b3] 00:00:03 PreventUserIdleSystemSleep named: "caffeinate command-line tool"
|
|
||||||
Details: caffeinate asserting for 1600 secs
|
|
||||||
Localized=THE CAFFEINATE TOOL IS PREVENTING SLEEP.
|
|
||||||
Timeout will fire in 1597 secs Action=TimeoutActionRelease
|
|
||||||
pid 145(WindowServer): [0x00044f2f00099212] 00:00:00 UserIsActive named: "com.apple.iohideventsystem.queue.tickle serviceID:10009be9e service:AppleUserHIDEventService product:CTRL Keyboard eventType:3"
|
|
||||||
Timeout will fire in 600 secs Action=TimeoutActionRelease
|
|
||||||
pid 80(powerd): [0x00044f2f00019216] 00:22:34 PreventUserIdleSystemSleep named: "Powerd - Prevent sleep while display is on"
|
|
||||||
```
|
|
||||||
|
|
||||||
See `man pmset` and `man caffeinate` for more details.
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
# Launch Some Confetti
|
|
||||||
|
|
||||||
If you have [Raycast](https://www.raycast.com/) installed on your machine, then
|
|
||||||
you have quick access to some confetti via their quick command palette. Trigger
|
|
||||||
the command palette to open, start typing `confetti` until it appears as the
|
|
||||||
focused option, and then hit enter.
|
|
||||||
|
|
||||||
🎉
|
|
||||||
|
|
||||||
We can launch confetti other ways, including programmatically from scripts.
|
|
||||||
|
|
||||||
To do this, we need to first find the _deeplink_ for the Raycast _confetti_
|
|
||||||
program. Trigger the command palette and type out `confetti` again. However,
|
|
||||||
this time instead of hitting enter, hit `Cmd+k` to open other actions. Find the
|
|
||||||
_Copy Deeplink_ option.
|
|
||||||
|
|
||||||
You should now have this on your clipboard:
|
|
||||||
|
|
||||||
```
|
|
||||||
raycast://extensions/raycast/raycast/confetti
|
|
||||||
```
|
|
||||||
|
|
||||||
With this deeplink in hand, we can now trigger confetti other places. The
|
|
||||||
easiest way to do this is to open a terminal and pass that deep link as an
|
|
||||||
argument to `open`.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ open raycast://extensions/raycast/raycast/confetti
|
|
||||||
```
|
|
||||||
|
|
||||||
Now you can wrap that up in any old bash script or even just tack it on to the
|
|
||||||
end of a run of your test suite:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ rails test && open raycast://extensions/raycast/raycast/confetti
|
|
||||||
```
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
# Prevent Sleep With The Caffeinate Command
|
|
||||||
|
|
||||||
MacOS has a built-in utility `caffeinate` that can programatically prevent your
|
|
||||||
machine from sleeping. There are two kinds of sleep that it can prevent via
|
|
||||||
_assertions_.
|
|
||||||
|
|
||||||
> caffeinate creates assertions to alter system sleep behavior.
|
|
||||||
|
|
||||||
The two kinds of sleep behavior are _display sleep_ and _system idle sleep_. An
|
|
||||||
assertion to prevent display sleep can be created with `-d` and system idle
|
|
||||||
sleep with `-i`.
|
|
||||||
|
|
||||||
We can combine those to prevent both and then specify a duration (_timeout_)
|
|
||||||
with `-t` (with a value in seconds).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
caffeinate -d -i -t 600
|
|
||||||
```
|
|
||||||
|
|
||||||
This creates assertions with 10 minute timeouts for both display and system idle
|
|
||||||
sleep.
|
|
||||||
|
|
||||||
The `caffeinate` command is blocking, so if you want to start it in the
|
|
||||||
background, you can do that like so:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
caffeinate -d -i -t 600 &
|
|
||||||
```
|
|
||||||
|
|
||||||
See `man caffeinate` for more details.
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
# Read The Lid Angle Sensor For A MacBook
|
|
||||||
|
|
||||||
MacOS has a bunch of internal HID (Human Interface Device) data that can surface
|
|
||||||
details about all kinds of "devices" that comprise your machine. Some obvious
|
|
||||||
ones are the keyboard and trackpad as well as external mice and keyboards. The
|
|
||||||
battery and power source details are another which is sometimes integrated into
|
|
||||||
tools that display battery status (e.g.
|
|
||||||
[`tmux-battery`](https://github.com/tmux-plugins/tmux-battery)), though it uses
|
|
||||||
`pmset` directly). And many, many more.
|
|
||||||
|
|
||||||
One example I'd never considered is that there is a sensor for the lid angle of
|
|
||||||
the laptop that can tell the system whether the lid is open or closed and how
|
|
||||||
open it is (i.e. at what angle). There is no public interface for this lid angle
|
|
||||||
sensor, but people exploring all the HID devices have found the identifiers that
|
|
||||||
correspond to it (e.g.
|
|
||||||
[`pybooklid`](https://github.com/tcsenpai/pybooklid/blob/main/pybooklid/macbook_lid.py)).
|
|
||||||
|
|
||||||
Here is a minimal script that uses `uv`, `hidapi` (python bindings), and
|
|
||||||
`libhidapi` (shared runtime lib for those bindings):
|
|
||||||
|
|
||||||
```python
|
|
||||||
#!/usr/bin/env -S uv run --quiet --script
|
|
||||||
# /// script
|
|
||||||
# requires-python = ">=3.10"
|
|
||||||
# dependencies = ["hidapi"]
|
|
||||||
# ///
|
|
||||||
"""Print MacBook lid angle in degrees."""
|
|
||||||
import os, sys
|
|
||||||
|
|
||||||
if sys.platform == "darwin":
|
|
||||||
brew = "/opt/homebrew/lib"
|
|
||||||
if os.path.exists(brew):
|
|
||||||
os.environ["DYLD_LIBRARY_PATH"] = f"{brew}:{os.environ.get('DYLD_LIBRARY_PATH','')}"
|
|
||||||
|
|
||||||
import hid
|
|
||||||
|
|
||||||
VENDOR_ID, PRODUCT_ID = 0x05AC, 0x8104
|
|
||||||
USAGE_PAGE, USAGE = 0x0020, 0x008A
|
|
||||||
REPORT_ID = 1
|
|
||||||
|
|
||||||
def read_angle():
|
|
||||||
for info in hid.enumerate(VENDOR_ID, PRODUCT_ID):
|
|
||||||
if info.get("usage_page") == USAGE_PAGE and info.get("usage") == USAGE:
|
|
||||||
d = hid.device()
|
|
||||||
path = info["path"]
|
|
||||||
d.open_path(path if isinstance(path, bytes) else path.encode())
|
|
||||||
try:
|
|
||||||
data = d.get_feature_report(REPORT_ID, 8)
|
|
||||||
if data and len(data) >= 3:
|
|
||||||
return float((data[2] << 8) | data[1])
|
|
||||||
finally:
|
|
||||||
d.close()
|
|
||||||
return None
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
a = read_angle()
|
|
||||||
if a is None:
|
|
||||||
sys.exit("sensor not available")
|
|
||||||
print(f"{a:.0f}")
|
|
||||||
```
|
|
||||||
|
|
||||||
These IDs and usage values are the undocumented values that allow the script to
|
|
||||||
navigate specifically to the lid angle sensor and specifically to the usage page
|
|
||||||
and value that represent the current lid angle reading.
|
|
||||||
|
|
||||||
```
|
|
||||||
VENDOR_ID, PRODUCT_ID = 0x05AC, 0x8104
|
|
||||||
USAGE_PAGE, USAGE = 0x0020, 0x008A
|
|
||||||
REPORT_ID = 1
|
|
||||||
```
|
|
||||||
|
|
||||||
I added [this
|
|
||||||
script](https://github.com/jbranchaud/dotfiles/blob/cbc7196607d1d6b25885f5387ca85b658bd765de/bin/lidangle)
|
|
||||||
to [my dotfiles](https://github.com/jbranchaud/dotfiles) and made it executable
|
|
||||||
(`chmod +x bin/lidangle`) so that I can try it out. I first ran it while it was
|
|
||||||
closed and connected to my external monitor (`0`), then I opened it as far as it
|
|
||||||
could go (`129`), and then I tried angling it close to what I thought was 90
|
|
||||||
degress (`92`, so close).
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ lidangle
|
|
||||||
0
|
|
||||||
|
|
||||||
❯ lidangle
|
|
||||||
129
|
|
||||||
|
|
||||||
❯ lidangle
|
|
||||||
92
|
|
||||||
```
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# Reveal Location Of File In Finder.app
|
|
||||||
|
|
||||||
In the terminal I have the path to an image file. I want to open Finder.app to
|
|
||||||
the location of that image file so that I can drag and drop it into a file
|
|
||||||
upload area in the browser.
|
|
||||||
|
|
||||||
Instead of opening a Finder.app window and navigating directory by directory to
|
|
||||||
the location, I can use the `open` command. Using `open` directly with the image
|
|
||||||
file will open the image in Preview.app. I want to reveal the directory that the
|
|
||||||
image file is in within Finder.app. _Reveal_ is the keyword and the `-R` flag
|
|
||||||
does just that.
|
|
||||||
|
|
||||||
Here is an example of this that I actually ran when uploading a screenshot that
|
|
||||||
went into [this blogmark post](https://still.visualmode.dev/blogmarks/255):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ open -R /Users/lastword/images/tiobe-index-graph-march-2026.png
|
|
||||||
```
|
|
||||||
|
|
||||||
See `man open` for more details.
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Generate Permutations Of All Valid 9-ball Racks
|
|
||||||
|
|
||||||
I wanted to produce a full listing of all valid rack arrangements for the game
|
|
||||||
of [9-ball](https://en.wikipedia.org/wiki/Nine-ball). The constraints on how a
|
|
||||||
9-ball rack can be arranged are, first, that the 1 ball must be placed at the
|
|
||||||
head of the diamond and, second, that the 9 ball must be placed at the center of
|
|
||||||
the diamond. After that, all other balls (2 through 8) can be placed in any
|
|
||||||
arrangement.
|
|
||||||
|
|
||||||
Because each of those seven remaining balls can be arranged in distinct
|
|
||||||
orderings where each ball is placed once, this is a
|
|
||||||
[_permutation_](https://en.wikipedia.org/wiki/Permutation) problem.
|
|
||||||
|
|
||||||
> In elementary combinatorics, the k-permutations, or partial permutations, are
|
|
||||||
> the ordered arrangements of k distinct elements selected from a set. When k is
|
|
||||||
> equal to the size of the set, these are the permutations in the previous
|
|
||||||
> sense.
|
|
||||||
|
|
||||||
For this problem, the seven distinct elements can be arranged into `7!` (seven
|
|
||||||
factorial) unique permutations. That is, 5040 permutations.
|
|
||||||
|
|
||||||
I can use [Ruby's `Array#permutations`
|
|
||||||
method](https://docs.ruby-lang.org/en/4.0/Array.html#method-i-permutation) to
|
|
||||||
enumerate these 5040 permutations like so:
|
|
||||||
|
|
||||||
```ruby
|
|
||||||
[2,3,4,5,6,7,8].permutation.map do |perm|
|
|
||||||
[1, *perm[0..2], 9, *perm[3..7]]
|
|
||||||
end.to_a
|
|
||||||
=> [[1, 2, 3, 4, 9, 5, 6, 7, 8],
|
|
||||||
[1, 2, 3, 4, 9, 5, 6, 8, 7],
|
|
||||||
[1, 2, 3, 4, 9, 5, 7, 6, 8],
|
|
||||||
[1, 2, 3, 4, 9, 5, 7, 8, 6],
|
|
||||||
[1, 2, 3, 4, 9, 5, 8, 6, 7],
|
|
||||||
[1, 2, 3, 4, 9, 5, 8, 7, 6],
|
|
||||||
[1, 2, 3, 4, 9, 6, 5, 7, 8],
|
|
||||||
...
|
|
||||||
[1, 8, 7, 6, 9, 5, 3, 2, 4],
|
|
||||||
[1, 8, 7, 6, 9, 5, 3, 4, 2],
|
|
||||||
[1, 8, 7, 6, 9, 5, 4, 2, 3],
|
|
||||||
[1, 8, 7, 6, 9, 5, 4, 3, 2]]
|
|
||||||
```
|
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
# Create Umbrella Task For All Test Tasks
|
# Create Umbrella Task For All Test Tasks
|
||||||
|
|
||||||
When I was first sketching out the [`mise` tasks](https://mise.jdx.dev/tasks/running-tasks.html) for a Rails app, I added
|
When I was first sketching out the [`mise`
|
||||||
the following two tasks. One is for running all the `rspec` tests. The other is
|
tasks](https://mise.jdx.dev/tasks/running-tasks.html) for a Rails app, I added
|
||||||
|
the following two tasks. One is for running all the `rspec` tests. The Other is
|
||||||
for running all the `vitest` (JavaScript) tests.
|
for running all the `vitest` (JavaScript) tests.
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
@@ -48,4 +49,5 @@ Running `mise run test:all` won't execute its own command, but because it
|
|||||||
depends on all other `test:*` tasks, the tests will get run through those
|
depends on all other `test:*` tasks, the tests will get run through those
|
||||||
dependencies.
|
dependencies.
|
||||||
|
|
||||||
This task naming pattern also allows for calling all tests with `mise run "test:**"`.
|
This task naming pattern also allows for calling all tests with `mise run
|
||||||
|
"test:**"`.
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
# Look In Ruby Version Dotfile
|
|
||||||
|
|
||||||
Newer versions of [`mise`](https://mise.jdx.dev/dev-tools/) specifically only
|
|
||||||
look for tool versions in `mise.toml` as well as the asdf `.tool-versions` file.
|
|
||||||
A lot of Ruby projects use the `.ruby-version` file to indicate the Ruby version
|
|
||||||
of a project. To continue to use the `.ruby-version` file instead of migrating
|
|
||||||
to `mise.toml`, you need to tell `mise` that you prefer to use the idiomatic
|
|
||||||
version file.
|
|
||||||
|
|
||||||
I added the following line to my
|
|
||||||
[`~/.config/mise/config.toml`](https://github.com/jbranchaud/dotfiles/commit/8edeb7a9c53500e89e88b4079cbd1859ebebcbda)
|
|
||||||
file:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
idiomatic_version_file_enable_tools = ["ruby"]
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, whenever `mise` is looking for the specified Ruby version of a project, it
|
|
||||||
will also look for `.ruby-version`.
|
|
||||||
|
|
||||||
Here is a [full list of idomatic version files supported by
|
|
||||||
`mise`](https://mise.jdx.dev/configuration.html#idiomatic-version-files).
|
|
||||||
|
|
||||||
See
|
|
||||||
[`idiomatic_version_file_enable_tools`](https://mise.jdx.dev/configuration/settings.html#idiomatic_version_file_enable_tools)
|
|
||||||
as well as the [Ruby-specific documentation](https://mise.jdx.dev/lang/ruby.html#ruby-version-and-gemfile-support)
|
|
||||||
for more details.
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
# Override Your Project Mise File
|
|
||||||
|
|
||||||
A project I'm working on has a version-controlled `.mise.toml` file in it. Some
|
|
||||||
changes were made to that recently that introduce some env vars that conflict
|
|
||||||
with my setup. If I make edits to that file, then I have a modified version of
|
|
||||||
`.mise.toml` sitting in my Git working copy.
|
|
||||||
|
|
||||||
```
|
|
||||||
# .mise.toml
|
|
||||||
[env]
|
|
||||||
CONFIG_SETTING = "project"
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, I can rely on the loading precedence rules of `mise` to override those
|
|
||||||
project settings with my individual settings. I can do that with the
|
|
||||||
`.mise.local.toml` file which is played on top of any `mise` configuration from
|
|
||||||
files further down the precedence chain.
|
|
||||||
|
|
||||||
```
|
|
||||||
# .mise.local.toml
|
|
||||||
[env]
|
|
||||||
CONFIG_SETTING = "override"
|
|
||||||
```
|
|
||||||
|
|
||||||
Assuming I have `mise` setup with my shell environment to automatically load in
|
|
||||||
these files, I can now check what takes precedence:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ echo $CONFIG_SETTING
|
|
||||||
override
|
|
||||||
```
|
|
||||||
|
|
||||||
Make sure `.mise.local.toml` is included in the `.gitignore` file to avoid
|
|
||||||
checking in your personal environment overrides.
|
|
||||||
|
|
||||||
To be sure about what files are loaded and in what order, give `mise cfg` a try.
|
|
||||||
I discuss that in more detail in [List The Files Being Loaded By Mise](list-the-files-being-loaded-by-mise.md).
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# Pick From Tasks Using Interactive Picker
|
|
||||||
|
|
||||||
In [Add Mise Tasks For Common Workflow
|
|
||||||
Commands](https://www.visualmode.dev/add-mise-tasks-for-common-workflow-commands),
|
|
||||||
I wrote about a set of tasks I added as shortcuts for connecting to the `rails console` in various environments.
|
|
||||||
|
|
||||||
```toml
|
|
||||||
# mise.toml
|
|
||||||
[tasks."console:staging"]
|
|
||||||
description = "Open a Rails console on staging"
|
|
||||||
run = "ssh -t my-app-staging dokku run my-app rails console"
|
|
||||||
|
|
||||||
[tasks."console:prod"]
|
|
||||||
description = "Open a Rails console on production"
|
|
||||||
run = "ssh -t my-app-prod dokku run my-app rails console"
|
|
||||||
```
|
|
||||||
|
|
||||||
When a project is configured with multiple `mise` tasks like this, we can invoke
|
|
||||||
`mise run` without any specific arguments and it will prompt you with an
|
|
||||||
interactive picker. The picker will populate with all the tasks like so:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ mise run
|
|
||||||
Tasks
|
|
||||||
Select a task to run
|
|
||||||
❯ console:prod Open a Rails console on production
|
|
||||||
console:staging Open a Rails console on staging
|
|
||||||
/
|
|
||||||
esc clear filter • enter confirm
|
|
||||||
```
|
|
||||||
|
|
||||||
We can navigate between the options with the arrow keys (and if we exit _filter_
|
|
||||||
mode by hitting `esc`, then `j/k` also work to move down and up). While in
|
|
||||||
_filter_ mode, we can type into the prompt which will filter the list of
|
|
||||||
commands down to just the partial matches.
|
|
||||||
|
|
||||||
Once we're targeting the task we want to run, we hit `enter` and the task is
|
|
||||||
executed.
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Search Through Bin Paths For Tool Locations
|
|
||||||
|
|
||||||
The `mise bin-paths` command will list all the bin paths that are managed by
|
|
||||||
`mise`. When you tell `mise` to install a tool, it installs a specific version
|
|
||||||
at a location where its binaries can be made accessible on the system path.
|
|
||||||
|
|
||||||
While `mise ls` is useful for seeing what is installed by `mise` and at what
|
|
||||||
version, the `bin-paths` command can tell you where those tool installations
|
|
||||||
with their binaries are located.
|
|
||||||
|
|
||||||
Combine this with `grep` or `rg` to narrow down the results to tools by a
|
|
||||||
specific name:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ mise bin-paths | rg 'neovim'
|
|
||||||
/Users/lastword/.local/share/mise/installs/npm-neovim/5.4.0/bin
|
|
||||||
/Users/lastword/.local/share/mise/installs/pipx-neovim-remote/2.5.1/bin
|
|
||||||
/Users/lastword/.local/share/mise/installs/neovim/0.11.6/bin
|
|
||||||
```
|
|
||||||
|
|
||||||
I can then look in one of these directories to see the one or more binaries that
|
|
||||||
they include. For instance, here is what is in the `node` bin path:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ ls /Users/lastword/.local/share/mise/installs/node/22.22.0/bin
|
|
||||||
./ ../ claude@ corepack@ node* npm* npx@
|
|
||||||
```
|
|
||||||
|
|
||||||
See `mise bin-paths --help` for more details.
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Get Idea Of What Is In A JSON Column
|
|
||||||
|
|
||||||
While digging through some data trying to reacquaint myself with the overall
|
|
||||||
schema and data model, I ran into an issue selecting rows from this
|
|
||||||
`content_resource` table. There was so much text packed in to the `"body"`,
|
|
||||||
`"summary"`, and `"description"` key-value pairs of `fields` JSON column that a
|
|
||||||
simple `select * ... limit 3;` was overwhelming the screen with text and table
|
|
||||||
formatting characters (i.e. `+------+-------`).
|
|
||||||
|
|
||||||
I figured the `fields` JSON followed a reliable structure, at least for records
|
|
||||||
of the same `type`. So, let's start by only grabbing the
|
|
||||||
[`json_keys`](https://dev.mysql.com/doc/refman/8.4/en/json-search-functions.html#function_json-keys)
|
|
||||||
so that I can get a sense of the shape of the JSON.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
select id, json_keys(fields)
|
|
||||||
from content_resource
|
|
||||||
where type = 'post'
|
|
||||||
limit 3;
|
|
||||||
|
|
||||||
+-----+-----------------------------------------------------------------------------------------------------------+
|
|
||||||
| id | json_keys(`fields`) |
|
|
||||||
+-----+-----------------------------------------------------------------------------------------------------------+
|
|
||||||
| 1 | ["body", "slug", "state", "title", "summary", "postType", "visibility", "description", "originalLessonId"] |
|
|
||||||
| 2 | ["body", "slug", "state", "title", "summary", "postType", "visibility", "description", "originalLessonId"] |
|
|
||||||
| 3 | ["body", "slug", "state", "title", "summary", "postType", "visibility", "description", "originalLessonId"] |
|
|
||||||
+-----+-----------------------------------------------------------------------------------------------------------+
|
|
||||||
```
|
|
||||||
|
|
||||||
For the `post` type, I see the same keys for this sampling of rows. Now I have
|
|
||||||
an idea what keys are present and can start digging in further.
|
|
||||||
|
|
||||||
My next query might look something like this:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
select id, fields->'$.slug', fields->'$.title', fields->'$.state'
|
|
||||||
from content_resource
|
|
||||||
where type = 'post'
|
|
||||||
limit 3;
|
|
||||||
```
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
# Jump Between Changes In Current File
|
|
||||||
|
|
||||||
With the [gitsigns.nvim plugin](https://github.com/lewis6991/gitsigns.nvim) for
|
|
||||||
Neovim, I get some handy Git-related capabilities like gutter highlighting of
|
|
||||||
additions, deletions, and changes to lines in the current file. These contiguous
|
|
||||||
sections of modification to the versioned state of a file are called hunks.
|
|
||||||
|
|
||||||
Here are two mappings (in Lua) for gitsigns that allow me to jump to the next
|
|
||||||
(`]h`) or previous (`[h`) hunk in the current file.
|
|
||||||
|
|
||||||
```lua
|
|
||||||
---@type LazyKeysSpec[]
|
|
||||||
M.gitsigns_mappings = {
|
|
||||||
|
|
||||||
-- Navigation
|
|
||||||
{
|
|
||||||
']h',
|
|
||||||
function()
|
|
||||||
if vim.wo.diff then
|
|
||||||
vim.cmd.normal { ']c', bang = true }
|
|
||||||
else
|
|
||||||
require('gitsigns').nav_hunk 'next'
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
desc = 'Next Hunk',
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
'[h',
|
|
||||||
function()
|
|
||||||
if vim.wo.diff then
|
|
||||||
vim.cmd.normal { '[c', bang = true }
|
|
||||||
else
|
|
||||||
require('gitsigns').nav_hunk 'prev'
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
desc = 'Prev Hunk',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This is particularly useful when I've just opened a big file and I want to jump
|
|
||||||
directly to active changes in that file.
|
|
||||||
|
|
||||||
I got this mapping directly from [Dorian's
|
|
||||||
dotfiles](https://github.com/dkarter/dotfiles).
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# Run nvim With Factory Defaults
|
|
||||||
|
|
||||||
Most of the fun of using Neovim is tailoring it to your exact needs with custom
|
|
||||||
configurations. Your configuration can be made up of environment variables,
|
|
||||||
`init.lua`/`init.vim`, and user directories on the `runtimepath`.
|
|
||||||
|
|
||||||
Perhaps though, you want to load neovim with its "factory defaults". You want
|
|
||||||
to ignore all your custom config and your _shada_ (shared data) file. I wanted
|
|
||||||
to do just that recently to verify that neovim has the `ft-manpage` plugin
|
|
||||||
enabled by default (as opposed to enabled somewhere in the labryinth of my
|
|
||||||
config files).
|
|
||||||
|
|
||||||
The `--clean` flag does just this. It loads built-in plugins, but none of the
|
|
||||||
user defined config.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ nvim --clean
|
|
||||||
```
|
|
||||||
|
|
||||||
This is different than `nvim -u NONE` which excludes all plugins, including
|
|
||||||
built-in ones.
|
|
||||||
|
|
||||||
See `man nvim` and `:help --clean` for more details.
|
|
||||||
-1
Submodule notes deleted from 897184eb02
@@ -1,23 +0,0 @@
|
|||||||
# See What Databases You Have Access To
|
|
||||||
|
|
||||||
Assuming you have the `pscale` CLI installed and you've authenticated with it,
|
|
||||||
you can run the following to view available databases.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pscale database list
|
|
||||||
NAME KIND CREATED AT UPDATED AT
|
|
||||||
----------- ------- ------------- -------------
|
|
||||||
bookshelf mysql 3 years ago 3 years ago
|
|
||||||
```
|
|
||||||
|
|
||||||
I'm not very active on my personal account. Planetscale is a multi-tenant SaaS
|
|
||||||
though. I can switch from my personal `org` to another team I have access to.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ pscale org switch another-team
|
|
||||||
```
|
|
||||||
|
|
||||||
And then from there I can run `pscale database list` again to see what databases
|
|
||||||
I have access to from this other organization.
|
|
||||||
|
|
||||||
See `pscale database help` and `pscale org help` for more details.
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Avoid Vulnerabilities In New Package Versions
|
|
||||||
|
|
||||||
It seems like every week there is a new supply chain attack where malicious code
|
|
||||||
is embedded in a popular, widely-used OSS package. This week's is
|
|
||||||
[axios](https://www.stepsecurity.io/blog/axios-compromised-on-npm-malicious-versions-drop-remote-access-trojan).
|
|
||||||
|
|
||||||
The [`pnpm` package manager](https://pnpm.io/) has a nice feature that helps
|
|
||||||
avoid installing these vulnerable package versions in the first place.
|
|
||||||
|
|
||||||
> To reduce the risk of installing compromised packages, you can delay the
|
|
||||||
> installation of newly published versions. In most cases, malicious releases
|
|
||||||
> are discovered and removed from the registry within an hour.
|
|
||||||
|
|
||||||
The [`minimumReleaseAge` config option](https://pnpm.io/settings#minimumreleaseage) tells `pnpm` to not install
|
|
||||||
a dependency (including transitive ones) until it has been released for at least
|
|
||||||
that many minutes.
|
|
||||||
|
|
||||||
For instance, if you wanted to set this to 72 hours, then you'd set this option
|
|
||||||
to `4320` minutes like so:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ pnpm config set minimum-release-age 4320 -g
|
|
||||||
```
|
|
||||||
|
|
||||||
The global flag (`-g`) will set that in your global config location, e.g.
|
|
||||||
`$XDG_CONFIG_HOME/pnpm/rc`. You could also add it specifically to your project
|
|
||||||
in the `pnpm-workspace.yaml` file.
|
|
||||||
|
|
||||||
[source](https://bsky.app/profile/styfle.dev/post/3miekuyeyrs2w)
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
# Check The Size Of Databases In A Cluster
|
|
||||||
|
|
||||||
The `\l` command in `psql` will list all the databases for the server. The
|
|
||||||
field surfaced by this meta-command are:
|
|
||||||
|
|
||||||
- Name
|
|
||||||
- Owner
|
|
||||||
- Encoding
|
|
||||||
- Locale Provider
|
|
||||||
- Collate
|
|
||||||
- Ctype
|
|
||||||
- ICU Locale
|
|
||||||
- ICU Rules
|
|
||||||
- Access privileges
|
|
||||||
|
|
||||||
If we add a `+`, issuing instead `\l+`, we get three additional fields:
|
|
||||||
|
|
||||||
- Size
|
|
||||||
- Tablespace
|
|
||||||
- Description
|
|
||||||
|
|
||||||
The _Size_ column is the human-formatted size of each database.
|
|
||||||
|
|
||||||
Another way to do this is with some SQL querying the underlying record keeping
|
|
||||||
of the server's database.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
select
|
|
||||||
db.datname as db_name,
|
|
||||||
pg_size_pretty(pg_database_size(db.datname)) as db_size
|
|
||||||
from pg_database db
|
|
||||||
order by pg_database_size(db.datname) desc;
|
|
||||||
```
|
|
||||||
|
|
||||||
Credit to [this StackOverflow
|
|
||||||
answer](https://stackoverflow.com/a/18907188/535590) for how to do this with a
|
|
||||||
SQL query.
|
|
||||||
|
|
||||||
[source](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMAND-LIST)
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# Compute Median Instead Of Average
|
|
||||||
|
|
||||||
One of the first aggregate functions we might use in PostgreSQL, besides `sum`,
|
|
||||||
is `avg`.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
select avg(book_count) as average_books_read
|
|
||||||
from (
|
|
||||||
select users.id, count(books.id) as book_count
|
|
||||||
from users
|
|
||||||
left join books
|
|
||||||
on books.user_id = users.id
|
|
||||||
where books.read_in_year = 2025
|
|
||||||
group by users.id
|
|
||||||
) as user_book_counts;
|
|
||||||
```
|
|
||||||
|
|
||||||
This computes the average of the set of values which sums them all up
|
|
||||||
and divides by the count. The average (maybe you've heard this also called the
|
|
||||||
_mean_) is not always the best way to understand data, especially when there are
|
|
||||||
outliers.
|
|
||||||
|
|
||||||
Instead, we might want to compute the _median_ value of our set of data. There
|
|
||||||
is no easily identifiable `median` aggregate function. Instead, we can use
|
|
||||||
`percentile_cont` with a value of `0.5`. This gets us the 50th percentile of our
|
|
||||||
set of data which is the definition of the _median_.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
select percentile_cont(0.5) within group (
|
|
||||||
order by book_count
|
|
||||||
) as median_books_read
|
|
||||||
from (
|
|
||||||
select users.id, count(books.id) as book_count
|
|
||||||
from users
|
|
||||||
left join books on books.user_id = users.id and books.read_in_year = 2025
|
|
||||||
group by users.id
|
|
||||||
) as user_book_counts;
|
|
||||||
```
|
|
||||||
|
|
||||||
The full syntax for `percentile_cont` is `percentile_cong(precision) within
|
|
||||||
group (order by ...)` because this is an aggregiate that has to work with an
|
|
||||||
ordered-set of data.
|
|
||||||
|
|
||||||
[source](https://www.postgresql.org/docs/current/functions-aggregate.html)
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
# Create And Execute SQL Statements With \gexec
|
|
||||||
|
|
||||||
The [`\gexec`
|
|
||||||
meta-command](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMAND-GEXEC)
|
|
||||||
is a variation of the [`\g`
|
|
||||||
meta-command](https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-META-COMMAND-G),
|
|
||||||
both of which can be used in a `psql` session. Whereas the `\g` command sends
|
|
||||||
the current query in the buffer to the PostgreSQL server for execution, the
|
|
||||||
`\gexec` command first sends the query to the server for execution and then
|
|
||||||
executes each row of the result as its own SQL statement.
|
|
||||||
|
|
||||||
This is both a bit absurd and powerful. And a bit unnecessary considering all
|
|
||||||
of the scripting capabilities with anything from bash to any language with a
|
|
||||||
SQL client library.
|
|
||||||
|
|
||||||
Nevertheless, let's take a look at a contrived example of how it works. Here,
|
|
||||||
we have a SQL statement that does some string concatenation based off values in
|
|
||||||
an array. This results in three separate `create schema` statements.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
> select
|
|
||||||
'create schema if not exists schema_' || letter || ';'
|
|
||||||
from unnest(array['a', 'b', 'c']) as letter
|
|
||||||
\gexec
|
|
||||||
|
|
||||||
CREATE SCHEMA
|
|
||||||
CREATE SCHEMA
|
|
||||||
CREATE SCHEMA
|
|
||||||
|
|
||||||
> \dn
|
|
||||||
List of schemas
|
|
||||||
Name | Owner
|
|
||||||
----------+-------------------
|
|
||||||
public | pg_database_owner
|
|
||||||
schema_a | postgres
|
|
||||||
schema_b | postgres
|
|
||||||
schema_c | postgres
|
|
||||||
(4 rows)
|
|
||||||
```
|
|
||||||
|
|
||||||
Three new schemas get created which we can inspect with `\dn`.
|
|
||||||
|
|
||||||
Notice, if we simply execute the primary statement, we can see the intermediate
|
|
||||||
result that `\gexec` will subsequently execute.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
> select
|
|
||||||
'create schema if not exists schema_' || letter || ';'
|
|
||||||
from unnest(array['a', 'b', 'c']) as letter
|
|
||||||
\g
|
|
||||||
|
|
||||||
?column?
|
|
||||||
---------------------------------------
|
|
||||||
create schema if not exists schema_a;
|
|
||||||
create schema if not exists schema_b;
|
|
||||||
create schema if not exists schema_c;
|
|
||||||
(3 rows)
|
|
||||||
```
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
# References Target Primary Key By Default
|
|
||||||
|
|
||||||
Typically when I am creating a table or adding a column that involves a foreign
|
|
||||||
key constraint, I explicitly name the reference column.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
create table contacts (
|
|
||||||
id int generated always as identity primary key,
|
|
||||||
user_id int references users(id);
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
The [Create Table PostgreSQL
|
|
||||||
Docs](https://www.postgresql.org/docs/17/sql-createtable.html) point out that
|
|
||||||
specifying the reference column isn't strictly necessary.
|
|
||||||
|
|
||||||
> These clauses specify a foreign key constraint, which requires that a group
|
|
||||||
> of one or more columns of the new table must only contain values that match
|
|
||||||
> values in the referenced column(s) of some row of the referenced table. If
|
|
||||||
> the refcolumn list is omitted, the primary key of the reftable is used.
|
|
||||||
|
|
||||||
If we're using the primary key as the reference column, then we can choose to
|
|
||||||
omit the reference column.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
create table contacts (
|
|
||||||
id int generated always as identity primary key,
|
|
||||||
user_id int references users;
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
In the same way we can do this when adding a column.
|
|
||||||
|
|
||||||
```sql
|
|
||||||
alter table contacts
|
|
||||||
add column account_id int references account;
|
|
||||||
```
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# Set Up A Project-Local Cluster With Postgres.app
|
|
||||||
|
|
||||||
I want to set up a PostgreSQL cluster in my project directory. This helps
|
|
||||||
provide some separation and clarity that this cluster and its databases are just
|
|
||||||
for this project.
|
|
||||||
|
|
||||||
This can be done with `Postgres.app` (on Mac) hitting the `+` button in the
|
|
||||||
bottom left corner of the app. This will pop open a "Create new server" modal.
|
|
||||||
|
|
||||||
From there, you'll want to give the server a name that you can identify within
|
|
||||||
`Postgres.app`. E.g. "<App Name> Cluster"
|
|
||||||
|
|
||||||
Then select the Postgres version. My existing project is still on 17, so I'll
|
|
||||||
select that.
|
|
||||||
|
|
||||||
The not so intuitive part is the _Data Directory_. Use the "Choose..." file
|
|
||||||
picker to find the root directory of your project. Select that. Then click into
|
|
||||||
the text input for the data directory and append the name of the data directory
|
|
||||||
_to be created_ to that path. If I want it to all go in `postgres-data`, then my
|
|
||||||
path will look like:
|
|
||||||
|
|
||||||
```
|
|
||||||
/Users/me/dev/my-app/postgres-data
|
|
||||||
```
|
|
||||||
|
|
||||||
The `postgres-data` directory doesn't exist yet. But it will in a moment.
|
|
||||||
|
|
||||||
You probably want the default port, so leave that at `5432` unless you know
|
|
||||||
otherwise.
|
|
||||||
|
|
||||||
Click `Create server`, though that won't actually create the server yet. Now
|
|
||||||
with that server selected in `Postgres.app` click the `Initialize` button. That
|
|
||||||
will create the `postgres-data` directory and then run `initdb` under the hood
|
|
||||||
which will add everything your server needs. It will now be running at that
|
|
||||||
port, ready to connect.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Access Most Recent Return Value In REPL
|
|
||||||
|
|
||||||
One of my favorite features of Ruby's `irb` and `pry` are that you can use `_`
|
|
||||||
to reference the most recent return value. Often as we use an interpreter or
|
|
||||||
REPL, we end up with _intermediate_ values. That is, we've execute some kind of
|
|
||||||
statement which returned a value and we now want to use that resulting value in
|
|
||||||
our next statement. Python also supports `_`.
|
|
||||||
|
|
||||||
Let's say I've run a statement that took a while to process, but I forgot to
|
|
||||||
assign it to a variable. Instead of re-running the whole thing, I can create a
|
|
||||||
variable that references the previous return value using `_`.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> BytePairEncoding.train_bpe(long_text)
|
|
||||||
{'merge_rules': [...], 'vocab': {...}}
|
|
||||||
>>> result = _
|
|
||||||
>>> list(result.keys())
|
|
||||||
['merge_rules', 'vocab']
|
|
||||||
```
|
|
||||||
|
|
||||||
Even if I don't necessarily want to assign it a variable, it can be nice to
|
|
||||||
reference the previous value as I continue with what I'm doing:
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> result['merge_rules'][0][1]
|
|
||||||
256
|
|
||||||
>>> result['vocab'][_]
|
|
||||||
b'e '
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice how the value from the first statement gets used as part of a `dict`
|
|
||||||
access.
|
|
||||||
|
|
||||||
[source](https://docs.python.org/3/tutorial/introduction.html#numbers)
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# Access Variables Outside Loop Scope
|
|
||||||
|
|
||||||
Here is a function that loops over a list to find the first occurrence of a
|
|
||||||
falsy value.
|
|
||||||
|
|
||||||
```python
|
|
||||||
def find_false(self):
|
|
||||||
for item in self.items:
|
|
||||||
item_type = type(item)
|
|
||||||
print(f"Current item: {item} ({item_type})")
|
|
||||||
if not item:
|
|
||||||
break
|
|
||||||
|
|
||||||
print(f"First false item: {item} ({item_type})")
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice how at the end of the function, outside of the loop, I am able to access
|
|
||||||
both `item` (defined in the loop definition) and `item_type` (defined within the
|
|
||||||
loop's body).
|
|
||||||
|
|
||||||
Both of these variables are defined, by the loop, in _function scope_ and are
|
|
||||||
accessible anywhere in the function after they have been defined.
|
|
||||||
|
|
||||||
The title of this TIL is a bit of a misnomer because Python doesn't have the
|
|
||||||
concept of a _loop scope_. There are two levels of scope in Python --
|
|
||||||
module/global scope and function scope.
|
|
||||||
|
|
||||||
I spend most of my time writing Ruby which also has _block scope_, so Python's
|
|
||||||
simplified two-level scoping took me by surprise.
|
|
||||||
|
|
||||||
Though the code sample above is contrived, this function scope assignment can be
|
|
||||||
taken advantage of with loop definitions in scenarios where you want to know
|
|
||||||
what the last `item` defined was before the loop terminated.
|
|
||||||
|
|
||||||
```python
|
|
||||||
for submission in submissions:
|
|
||||||
if passes(submission, criteria):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ValueError("No submissions that meet given criteria")
|
|
||||||
|
|
||||||
print(f"Submit first passing submission: {submission.id}")
|
|
||||||
submit(submission)
|
|
||||||
```
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
# Argument Defaults Are Evaluated When Function Is Defined
|
|
||||||
|
|
||||||
When you define a function with any arguments that have default values, those
|
|
||||||
default values are evaluated and stored at the time that the function is defined
|
|
||||||
(i.e. when it is evaluated by the interpreter). This might feel counter
|
|
||||||
intuitive if you are coming from another language, like Ruby, where these kinds
|
|
||||||
of defaults are evaluated at call time. This is unremarkable for scalar values
|
|
||||||
like `4` or `"fallback"`. It's much more interesting when your defaults are
|
|
||||||
function calls.
|
|
||||||
|
|
||||||
What if our default is something like `datetime.now()`?
|
|
||||||
|
|
||||||
Here I've defined a `Timer` class that has a `start` and `stop` method. The
|
|
||||||
`stop` method can be called with a specific `datetime` value otherwise it falls
|
|
||||||
back to `datetime.now()` -- but when is _now_?
|
|
||||||
|
|
||||||
```python
|
|
||||||
from datetime import datetime, timezone
|
|
||||||
import time
|
|
||||||
|
|
||||||
|
|
||||||
class Timer:
|
|
||||||
def __init__(self):
|
|
||||||
self._start = None
|
|
||||||
self._stop = None
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self._start = datetime.now(timezone.utc)
|
|
||||||
self._stop = None
|
|
||||||
|
|
||||||
def stop(self, at=datetime.now(timezone.utc)):
|
|
||||||
print(f"now: {datetime.now(timezone.utc)}")
|
|
||||||
print(f" at: {at}")
|
|
||||||
self._stop = at
|
|
||||||
|
|
||||||
elapsed = self._stop - self._start
|
|
||||||
return elapsed
|
|
||||||
```
|
|
||||||
|
|
||||||
Here I instantiate a timer, call `start`, sleep for 5 seconds, and then call
|
|
||||||
`stop`.
|
|
||||||
|
|
||||||
```python
|
|
||||||
timer = Timer()
|
|
||||||
timer.start()
|
|
||||||
|
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
print(f"Elapsed: {timer.stop()}")
|
|
||||||
```
|
|
||||||
|
|
||||||
Here is what gets printed to `stdout`:
|
|
||||||
|
|
||||||
```
|
|
||||||
now: 2026-05-22 00:45:05.654878+00:00
|
|
||||||
at: 2026-05-22 00:45:00.649699+00:00
|
|
||||||
Elapsed: -1 day, 23:59:59.999875
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice that the actual _now_ (when the `stop` method is running) is about 5
|
|
||||||
seconds after the value of `at`. That is because `at`, which takes on the
|
|
||||||
default argument value, is `datetime.now()` as evaluated at the time the
|
|
||||||
function is interpreted. It is for that same reason that `self._stop` ends up
|
|
||||||
being just a hair earlier than the call to `start` which sets `self._start`.
|
|
||||||
Which explains why the _elapsed_ time is a negative value.
|
|
||||||
|
|
||||||
To avoid this awkwardness all together, set the default as `None` and then
|
|
||||||
override `None` at the start of the function:
|
|
||||||
|
|
||||||
```python
|
|
||||||
def stop(self, at = None):
|
|
||||||
if at == None:
|
|
||||||
at = datetime.now(timezone.utc)
|
|
||||||
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# Assert Is Only A Development Check
|
|
||||||
|
|
||||||
The `assert` keyword is used in Python to write a statement that will check some
|
|
||||||
assertion and raise an error if it isn't met. This is only meant to be used as a
|
|
||||||
check during development because it can be easily optimized out of the code.
|
|
||||||
|
|
||||||
```python
|
|
||||||
stuff = None
|
|
||||||
|
|
||||||
assert stuff, "We need to have some stuff to proceed"
|
|
||||||
|
|
||||||
print(f"We have {stuff or 'something'}!")
|
|
||||||
```
|
|
||||||
|
|
||||||
If I execute this code with `python`, it will raise on that second line of code.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ python assert_example.py
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "/Users/lastword/dev/jbranchaud/py-vmt/assert_example.py", line 3, in <module>
|
|
||||||
assert stuff, "We need to have some stuff to proceed"
|
|
||||||
^^^^^
|
|
||||||
AssertionError: We need to have some stuff to proceed
|
|
||||||
```
|
|
||||||
|
|
||||||
This `assert` statement will be stripped out of the compiled bytecode if the
|
|
||||||
`-O` (capital o) flag is used. Notice how running the same file with that flag
|
|
||||||
does not lead to an `AssertionError`.
|
|
||||||
|
|
||||||
```python
|
|
||||||
❯ python -O assert_example.py
|
|
||||||
We have something!
|
|
||||||
```
|
|
||||||
|
|
||||||
If I want to make sanity checks for situations that would be caused by a bug in
|
|
||||||
the code, an `assert` statement can be a good candidate. However, if I am making
|
|
||||||
runtime checks like validating user input, then an `if` statement and raising
|
|
||||||
something like a `ValueError` is better.
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
# Avoid Modification With Frozen Dataclass
|
|
||||||
|
|
||||||
The `@dataclass` decorator can be set as _frozen_ to prevent modification of
|
|
||||||
values on instances of that `dataclass`.
|
|
||||||
|
|
||||||
Without making it frozen, I can easily subvert validations by changing the value
|
|
||||||
of attributes after the `__post_init__` validations are called.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> config = BPEConfig(300, []) # passes validations
|
|
||||||
>>> config.vocab_size = 22 # this is invalid, wish this was prevented
|
|
||||||
```
|
|
||||||
|
|
||||||
Here is the updated `@dataclass` declaration with `frozen=True` passed as a
|
|
||||||
parameter.
|
|
||||||
|
|
||||||
```python
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from typing import ClassVar
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class BPEConfig:
|
|
||||||
BASE_VOCAB_SIZE: ClassVar[int] = 256
|
|
||||||
|
|
||||||
vocab_size: int
|
|
||||||
special_tokens: list[str]
|
|
||||||
|
|
||||||
def __post_init__(self):
|
|
||||||
if self.vocab_size < self.BASE_VOCAB_SIZE:
|
|
||||||
msg = f"vocab_size ({self.vocab_size}) must be greater than or equal to BASE_VOCAB_SIZE ({self.BASE_VOCAB_SIZE})"
|
|
||||||
raise ValueError(msg)
|
|
||||||
```
|
|
||||||
|
|
||||||
Now I am prevented from modifying a scalar value like `vocab_size` after the
|
|
||||||
instance has been created.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> config = BPEConfig(300, [])
|
|
||||||
>>> config.vocab_size = 22
|
|
||||||
Traceback (most recent call last):
|
|
||||||
File "<stdin>", line 1, in <module>
|
|
||||||
File "<string>", line 4, in __setattr__
|
|
||||||
dataclasses.FrozenInstanceError: cannot assign to field 'vocab_size'
|
|
||||||
```
|
|
||||||
|
|
||||||
This doesn't prevent you from modifying the contents of attributes that are
|
|
||||||
`list` or `dict` types.
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
# Check If Package Is Installed With Pip
|
|
||||||
|
|
||||||
I recently installed PyTorch, but when I tried using it, I was getting an error
|
|
||||||
about `numpy` not being installed. I was kind of surprised by that because I
|
|
||||||
thought I would have already had that.
|
|
||||||
|
|
||||||
I wanted to check, so I asked with `pip show`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ python3 -m pip show numpy
|
|
||||||
WARNING: Package(s) not found: numpy
|
|
||||||
```
|
|
||||||
|
|
||||||
I can even list everything that is installed with `pip` using `pip list` like
|
|
||||||
so:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ python3 -m pip list
|
|
||||||
Package Version Build
|
|
||||||
------------------ --------- -----
|
|
||||||
certifi 2026.1.4
|
|
||||||
cffi 2.0.0
|
|
||||||
charset-normalizer 3.4.4
|
|
||||||
click 8.3.1
|
|
||||||
commonmark 0.9.1
|
|
||||||
cryptography 46.0.3
|
|
||||||
docutils 0.22.4
|
|
||||||
filelock 3.24.2
|
|
||||||
fsspec 2026.2.0
|
|
||||||
idna 3.11
|
|
||||||
Jinja2 3.1.6
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
I then installed `numpy` (`python3 -m pip install numpy`) and how I can use `pip
|
|
||||||
show` again to confirm that.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ python3 -m pip show numpy
|
|
||||||
Name: numpy
|
|
||||||
Version: 2.4.2
|
|
||||||
Summary: Fundamental package for array computing in Python
|
|
||||||
Home-page: https://numpy.org
|
|
||||||
Author: Travis E. Oliphant et al.
|
|
||||||
Author-email:
|
|
||||||
License-Expression: BSD-3-Clause AND 0BSD AND MIT AND Zlib AND CC0-1.0
|
|
||||||
Location: /Users/lastword/.local/share/mise/installs/python/3.12.12/lib/python3.12/site-packages
|
|
||||||
Requires:
|
|
||||||
Required-by:
|
|
||||||
```
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
# Check Precondition Before Click Arg Parsing
|
|
||||||
|
|
||||||
When setting up various [Click](https://click.palletsprojects.com/en/stable/)
|
|
||||||
subcommands with options, I ran into an issue with the order of some validation
|
|
||||||
checks. I was putting the same precondition validation logic at the beginning of
|
|
||||||
several subcommands. I was also putting callback validations on specific options
|
|
||||||
to those subcommands. Ideally the option validations could rely on those
|
|
||||||
precondition validations. However, the option callbacks run before anything in
|
|
||||||
the body of the subcommands.
|
|
||||||
|
|
||||||
The solution was to move those preconditions out of the subcommand body
|
|
||||||
(simplifying the subcommand) and into a `click.Command` subclass.
|
|
||||||
|
|
||||||
To demonstrate that, I'll first show the `click.Command` subclass:
|
|
||||||
|
|
||||||
```python
|
|
||||||
class RequireActiveSessionCommand(click.Command):
|
|
||||||
def parse_args(self, ctx, args):
|
|
||||||
if ctx.obj.active_session is None:
|
|
||||||
msg = "No active session being tracked. Start a session first."
|
|
||||||
raise click.UsageError(msg)
|
|
||||||
|
|
||||||
return super().parse_args(ctx, args)
|
|
||||||
```
|
|
||||||
|
|
||||||
The only thing this subclass overrides is `parse_args` where it gets ahead of
|
|
||||||
the standard arg parsing logic to first check the precondition. In this case, I
|
|
||||||
check that there is an active session. If there isn't, then I can raise a
|
|
||||||
`click.UsageError`. Otherwise, it delegates back to the super-class
|
|
||||||
implementation of `parse_args`.
|
|
||||||
|
|
||||||
This subclass then gets used for the commands that need to enforce this
|
|
||||||
precondition. Two prime examples of that are the `stop` and `cancel` subcommands.
|
|
||||||
|
|
||||||
```python
|
|
||||||
@cli.command(cls=RequireActiveSessionCommand)
|
|
||||||
@click.option("--at", help='Hours previous to end the timer, e.g. "2 hours ago"', callback=validate_stop_at)
|
|
||||||
@pass_cli
|
|
||||||
def stop(cli_ctx: CliContext, at: datetime) -> None:
|
|
||||||
# ... implementation omitted
|
|
||||||
|
|
||||||
@cli.command(cls=RequireActiveSessionCommand)
|
|
||||||
@pass_cli
|
|
||||||
def cancel(cli_ctx: CliContext):
|
|
||||||
# ... implementation omitted
|
|
||||||
```
|
|
||||||
|
|
||||||
Other subcommands, like `start` and `status` that don't need to enforce this
|
|
||||||
precondition use the `@cli.command()` decorator without passing in a custom
|
|
||||||
subclass.
|
|
||||||
|
|
||||||
This example is pulled directly from [this commit](https://github.com/jbranchaud/py-vmt/commit/505109b7a4013e05f085cded666c6b1ac7c3c250)
|
|
||||||
of my [`py-vmt` time tracker tool](https://github.com/jbranchaud/py-vmt).
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Control Passing Of Time In Tests
|
|
||||||
|
|
||||||
While it is nice to be able to write pure functional code, our software still
|
|
||||||
lives in the real world and may have to relate to or depend on the passing of
|
|
||||||
time. In order to test this kind of code, we need time to behave in a reliable,
|
|
||||||
deterministic way. One of the best ways to create a testing environment where
|
|
||||||
that is true is to bring in tooling that hijacks time.
|
|
||||||
|
|
||||||
The [`freezegun` module](https://github.com/spulec/freezegun) is a great tool
|
|
||||||
for that job. We can use it to freeze time at a specific testable point, advance
|
|
||||||
time a specific amount, and much more.
|
|
||||||
|
|
||||||
Here is an example from the tests for [my CLI-based time tracking
|
|
||||||
app](https://github.com/jbranchaud/py-vmt/blob/acb26e4840279d936a12f16c505ca7e75e9a6d20/tests/src/py_vmt/test_cli.py#L21)
|
|
||||||
where I freeze time before starting a session. That gives me a chance to assert
|
|
||||||
about the exact start time that is output by the command. Then I can advance
|
|
||||||
time a little and assert that the `status` command outputs the correct thing.
|
|
||||||
|
|
||||||
```python
|
|
||||||
import datetime
|
|
||||||
from freezegun import freeze_time
|
|
||||||
|
|
||||||
# some other test setup omitted ...
|
|
||||||
|
|
||||||
initial_datetime = datetime.datetime(
|
|
||||||
2026, 3, 14, 15, 5, 11, 0, datetime.timezone.utc
|
|
||||||
)
|
|
||||||
with freeze_time(initial_datetime) as frozen_datetime:
|
|
||||||
# start a session
|
|
||||||
start_result = runner.invoke(cli, ["start", "my-project"])
|
|
||||||
output = "Started tracking 'my-project' at 10:05AM"
|
|
||||||
assert output in start_result.output
|
|
||||||
|
|
||||||
frozen_datetime.tick(delta=datetime.timedelta(minutes=30))
|
|
||||||
|
|
||||||
# check status
|
|
||||||
status_result = runner.invoke(cli, ["status"])
|
|
||||||
output = "Tracking 'my-project' for 30m (since 10:05AM)"
|
|
||||||
assert output in status_result.output
|
|
||||||
```
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
# Create A Range Of Descending Values
|
|
||||||
|
|
||||||
A typical use of `range` looks something like this:
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> list(range(1, 5))
|
|
||||||
[1, 2, 3, 4]
|
|
||||||
```
|
|
||||||
|
|
||||||
Which is equivalent to this one where we give a `step` value of `1`.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> list(range(1, 5, 1))
|
|
||||||
[1, 2, 3, 4]
|
|
||||||
```
|
|
||||||
|
|
||||||
If we try to create a _negative range_, that is, a range of values in decreasing
|
|
||||||
order, we get an empty list.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> list(range(0, -7))
|
|
||||||
[]
|
|
||||||
```
|
|
||||||
|
|
||||||
That's because the `step` value still defaults to `1`. And there are no positive
|
|
||||||
steps between `0` and `-7`. So, let's give `range` a `step` value of `-1`.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> list(range(0,-7, -1))
|
|
||||||
[0, -1, -2, -3, -4, -5, -6]
|
|
||||||
```
|
|
||||||
|
|
||||||
One practical use case of a negative range like this is using a list
|
|
||||||
comprehension to transform it into a list of the _last seven days_.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> from datetime import datetime, timedelta
|
|
||||||
>>> [datetime.now().date() + timedelta(days=days) for days in range(0,-7, -1)]
|
|
||||||
[datetime.date(2026, 3, 19), datetime.date(2026, 3, 18), datetime.date(2026, 3, 17), datetime.date(2026, 3, 16), datetime.date(2026, 3, 15), datetime.date(2026, 3, 14), datetime.date(2026, 3, 13)]
|
|
||||||
```
|
|
||||||
|
|
||||||
Of course this could have been written with a positive range and then
|
|
||||||
subtracting the `timedelta`. I like that I have the option of doing this in
|
|
||||||
whatever way makes the code most readable.
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
# Deduplicate A List Into A Tuple
|
|
||||||
|
|
||||||
A `list` is not hashable which means you can't use it for things like `dict`
|
|
||||||
keys or cache keys. Instead you need to convert it into something like a `set`
|
|
||||||
or a `tuple`.
|
|
||||||
|
|
||||||
Here is an example list:
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> l1 = [3,4,1,2,5,4,1]
|
|
||||||
```
|
|
||||||
|
|
||||||
Turning this list into a `set` or `frozenset` is straightforward:
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> set(l1)
|
|
||||||
{1, 2, 3, 4, 5}
|
|
||||||
>>> frozenset(l1)
|
|
||||||
frozenset({1, 2, 3, 4, 5})
|
|
||||||
```
|
|
||||||
|
|
||||||
If you're trying to preserve the order after deduplicating, then you'll want to
|
|
||||||
use a `tuple` instead of a `set`. In order to deduplicate while maintaining the
|
|
||||||
ordering, you can exploit the fact that `dict` keys maintain their order. A
|
|
||||||
`list` can be transformed into the keys of a `dict` with
|
|
||||||
[`dict.fromkeys`](https://docs.python.org/3/library/stdtypes.html#dict.fromkeys):
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> dict.fromkeys(l1)
|
|
||||||
{3: None, 4: None, 1: None, 2: None, 5: None}
|
|
||||||
```
|
|
||||||
|
|
||||||
And here is your `tuple` which extracts the keys of the `dict`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> tuple(dict.fromkeys(l1))
|
|
||||||
(3, 4, 1, 2, 5)
|
|
||||||
```
|
|
||||||
|
|
||||||
By comparison, here is the `tuple` transformed directly from the `list` without
|
|
||||||
deduplication.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> tuple(l1)
|
|
||||||
(3, 4, 1, 2, 5, 4, 1)
|
|
||||||
```
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
# Define Sequence Of Tests With Parametrize Decorator
|
|
||||||
|
|
||||||
I have a function that I want to test across a bunch of different inputs. That
|
|
||||||
way I can make sure the logic of that function handles all the different
|
|
||||||
scenarios I have in mind.
|
|
||||||
|
|
||||||
While working on [`py-vmt`](https://github.com/jbranchaud/py-vmt), I started by
|
|
||||||
writing a big single test function with a sequence of variable assignments and
|
|
||||||
`assert` statements. Here's my starting point:
|
|
||||||
|
|
||||||
```python
|
|
||||||
def test_format_time_delta_everything():
|
|
||||||
# less than a minute
|
|
||||||
thirty_seconds = timedelta(seconds=30)
|
|
||||||
assert "30s" == format_time_delta(thirty_seconds)
|
|
||||||
|
|
||||||
# one minute exactly
|
|
||||||
one_minute = timedelta(seconds=60)
|
|
||||||
assert "1m" == format_time_delta(one_minute)
|
|
||||||
|
|
||||||
# more than a minute
|
|
||||||
assert "1m30s" == format_time_delta(one_minute + thirty_seconds)
|
|
||||||
|
|
||||||
# bunch of minutes and seconds
|
|
||||||
delta = timedelta(minutes=24, seconds=8)
|
|
||||||
assert "24m8s" == format_time_delta(delta)
|
|
||||||
|
|
||||||
# one hour exactly
|
|
||||||
one_hour = timedelta(hours=1)
|
|
||||||
assert "1h" == format_time_delta(one_hour)
|
|
||||||
|
|
||||||
# more than one hour
|
|
||||||
assert "1h24m" == format_time_delta(one_hour + delta)
|
|
||||||
```
|
|
||||||
|
|
||||||
I knew I would eventually need to break it up into individual test functions,
|
|
||||||
but I couldn't bare to start there because it seemed quite repetitive.
|
|
||||||
|
|
||||||
There is another way to approach this without all the duplication. Pytest comes
|
|
||||||
with [a "parametrize" decorator](https://docs.pytest.org/en/stable/example/parametrize.html). This is
|
|
||||||
used to define a set of test data (and expected values) that will get passed
|
|
||||||
one-by-one to the test function as parameters.
|
|
||||||
|
|
||||||
```python
|
|
||||||
@pytest.mark.parametrize("input,expected", [
|
|
||||||
(timedelta(seconds=30), "30s"),
|
|
||||||
(timedelta(seconds=60), "1m"),
|
|
||||||
(timedelta(seconds=90), "1m30s"),
|
|
||||||
(timedelta(minutes=24, seconds=8), "24m8s"),
|
|
||||||
(timedelta(hours=1), "1h"),
|
|
||||||
(timedelta(hours=1, minutes=24, seconds=8), "1h24m"),
|
|
||||||
])
|
|
||||||
def test_format_time_delta(input, expected):
|
|
||||||
assert format_time_delta(input) == expected
|
|
||||||
```
|
|
||||||
|
|
||||||
I ditch all of the duplication this way. I define a list of tuples that
|
|
||||||
represent my input values and expected values. Then the body of the test can be
|
|
||||||
minimal. And I get a separate test execution for each parameter tuple making it
|
|
||||||
easier to see fine-grained pass/fail results.
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Define Typed Class Interface With Protocol
|
|
||||||
|
|
||||||
In [`py-vmt`](https://github.com/jbranchaud/py-vmt) I am defining different
|
|
||||||
storage access layers for the CLI to use. I want a consistent interface that the
|
|
||||||
core CLI logic can depend on regardless of whether it is a JSON file or a SQLite
|
|
||||||
database. To achieve that I can define a class of unimplemented functions that
|
|
||||||
inherits from
|
|
||||||
[`typing.Protocol`](https://typing.python.org/en/latest/spec/protocol.html).
|
|
||||||
|
|
||||||
```python
|
|
||||||
from typing import Protocol
|
|
||||||
|
|
||||||
class SessionRepository(Protocol):
|
|
||||||
def active_session(self) -> Session | None: ...
|
|
||||||
def write_active_session(self, session) -> None: ...
|
|
||||||
def append_session(self, session) -> None: ...
|
|
||||||
def all_sessions(self) -> list[Session]: ...
|
|
||||||
def clear_active_session(self) -> None: ...
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice that none of these have default implementations. The `...` indicates that
|
|
||||||
class implementing this protocol will define the implementation of those
|
|
||||||
functions.
|
|
||||||
|
|
||||||
Now, my `CliContext` class, which needs some kind of `SessionRepository` to
|
|
||||||
function can indicate as much in `__init__`.
|
|
||||||
|
|
||||||
```python
|
|
||||||
class CliContext:
|
|
||||||
def __init__(self, verbose: bool, repo: SessionRepository | None = None) -> None:
|
|
||||||
self.verbose: bool = verbose
|
|
||||||
self.active_session: Session | None = None
|
|
||||||
self.repo: SessionRepository = repo or JsonRepository()
|
|
||||||
self.active_session = self.repo.active_session()
|
|
||||||
```
|
|
||||||
|
|
||||||
If `JsonRepository` doesn't define all of the methods specified in the protocol,
|
|
||||||
then a type error will occur wherever it clashes with `SessionRepository`. Now
|
|
||||||
as I implement `SqliteRepository` I have a standard interface to build against
|
|
||||||
that I know I can seamlessly swap in.
|
|
||||||
|
|
||||||
[source](https://typing.python.org/en/latest/reference/protocols.html#simple-user-defined-protocols)
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
# Easy Key-Value Aggregates With defaultdict
|
|
||||||
|
|
||||||
The `collections` module has the `defaultdict` object that can be used to
|
|
||||||
aggregate values tied to a key. What sets this apart from simply using a `dict`
|
|
||||||
is that we get the base value for free. So if our aggregate value is a list,
|
|
||||||
then we get `[]` by default for each new key. In the same way, we'd get `0` if
|
|
||||||
it was constructed with `int`.
|
|
||||||
|
|
||||||
Here is the counter example from [Keep A Tally With
|
|
||||||
collections.Counter](keep-a-tally-with-collections-counter.md)
|
|
||||||
|
|
||||||
```python
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
def get_pair_counts(token_ids: list[int]) -> Counter:
|
|
||||||
"""Count how often each adjacent pair appears"""
|
|
||||||
counts = defaultdict(int)
|
|
||||||
for i in range(len(token_ids) - 1):
|
|
||||||
pair = (token_ids[i], token_ids[i + 1])
|
|
||||||
counts[pair] += 1
|
|
||||||
return counts
|
|
||||||
```
|
|
||||||
|
|
||||||
We never have to initially set a key to `0`. If the key is not yet present, then
|
|
||||||
`int()` (the zero-value constructor) is used as the `__missing__` value.
|
|
||||||
|
|
||||||
We can do the same with `list`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> import collections
|
|
||||||
>>> stuff = collections.defaultdict(list)
|
|
||||||
>>> stuff['alpha'].append(1)
|
|
||||||
>>> stuff['alpha']
|
|
||||||
[1]
|
|
||||||
>>> stuff['beta']
|
|
||||||
[]
|
|
||||||
```
|
|
||||||
|
|
||||||
In the same way, this uses `list()` as the `__missing__` value to start of each
|
|
||||||
key with an `[]`.
|
|
||||||
|
|
||||||
I find this so handy because in other languages I've typically had to do
|
|
||||||
something more like this:
|
|
||||||
|
|
||||||
```python
|
|
||||||
words_by_length = {}
|
|
||||||
for item in items:
|
|
||||||
if len(item) not in words_by_length:
|
|
||||||
words_by_length[len(item)] = []
|
|
||||||
words_by_length[len(item)].append(item)
|
|
||||||
```
|
|
||||||
|
|
||||||
This is much clunkier.
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# Enable Pyright Type Checking In Cursor
|
|
||||||
|
|
||||||
In most ways [Cursor](https://cursor.com/), a clone of VS Code, behaves like VS
|
|
||||||
Code and uses the same extensions as VS Code. It even offers to clone all your
|
|
||||||
existing extensions and setup from VS Code when you first install it.
|
|
||||||
|
|
||||||
However, the Pyright type checking setup that I had in VS Code stopped working
|
|
||||||
when I opened up the same Python project in Cursor. It seems that to get Pyright
|
|
||||||
to reliably work with forks of VS Code, you need to use a compatible fork like
|
|
||||||
[Based Pyright](https://docs.basedpyright.com/latest/).
|
|
||||||
|
|
||||||
Once I installed _Based Pyright_ from the extension marketplace, I was able to
|
|
||||||
enable it in `.vscode/settings.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
...,
|
|
||||||
"basedpyright.analysis.typeCheckingMode": "basic"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
I may have needed to restart Cursor at this point, I cannot remember exactly.
|
|
||||||
However, once this setup was in place the helpful type checking errors started
|
|
||||||
appearing as red squiggles.
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
# Get Absolute Seconds From `timedelta` Object
|
|
||||||
|
|
||||||
The [`timedelta` object provided by
|
|
||||||
`datetime`](https://docs.python.org/3/library/datetime.html#timedelta-objects)
|
|
||||||
is a useful built-in concept for representing a duration of time.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> from datetime import timedelta
|
|
||||||
>>> diff = timedelta(hours=1, minutes=1, seconds=6)
|
|
||||||
>>> diff.seconds
|
|
||||||
3666
|
|
||||||
```
|
|
||||||
|
|
||||||
It is pretty minimal though. There are only a couple things you can inspect
|
|
||||||
about it -- `days`, `seconds` (as I did in the snippet above), and
|
|
||||||
`microseconds`.
|
|
||||||
|
|
||||||
And perhaps that is enough to hint at the issue I recently ran into with it --
|
|
||||||
specifically that you can access both `days` and `seconds`.
|
|
||||||
|
|
||||||
Let's look at what happens when I have a `timedelta` with more than a day worth
|
|
||||||
of seconds.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> diff = timedelta(seconds=(3600 * 24 + 1))
|
|
||||||
>>> diff.seconds
|
|
||||||
1
|
|
||||||
>>> diff.days
|
|
||||||
1
|
|
||||||
```
|
|
||||||
|
|
||||||
I thought `seconds` was going to produce `86401` instead of `1`. The reason is
|
|
||||||
because any amount of duration over a day gets converted into the `days` value
|
|
||||||
and its the remaining time smaller than a day that is represented by `seconds`.
|
|
||||||
|
|
||||||
In my [original implementation of
|
|
||||||
`format_time_delta`](https://github.com/jbranchaud/py-vmt/blob/c14eaa56cf5f5c6d0120a95f04f95a6c87443e1c/src/py_vmt/time_helpers.py#L11-L14),
|
|
||||||
I was trying to build a relative time string by converting `seconds` into hours,
|
|
||||||
minutes, and seconds. That approach falls apart as soon as the delta is greater
|
|
||||||
than a day.
|
|
||||||
|
|
||||||
```python
|
|
||||||
def format_time_delta(diff) -> str:
|
|
||||||
hours, remainder = divmod(diff.seconds, 3600)
|
|
||||||
minutes, remainder = divmod(remainder, 60)
|
|
||||||
seconds = remainder
|
|
||||||
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, I needed to reach for [the `total_seconds()` function](https://docs.python.org/3/library/datetime.html#datetime.timedelta.total_seconds).
|
|
||||||
This gives "the total number of seconds contained in the duration" and is
|
|
||||||
described as equivalent to `diff / timedelta(seconds=1)`.
|
|
||||||
|
|
||||||
Here is the [updated version of `format_time_delta`](https://github.com/jbranchaud/py-vmt/blob/ec1875a9d73552f5481e3945ddf522e94d0cc018/src/py_vmt/time_helpers.py?plain=1#L11-L16):
|
|
||||||
|
|
||||||
```python
|
|
||||||
def format_time_delta(diff: timedelta) -> str:
|
|
||||||
total_seconds = int(diff.total_seconds())
|
|
||||||
|
|
||||||
hours, remainder = divmod(total_seconds, 3600)
|
|
||||||
minutes, remainder = divmod(remainder, 60)
|
|
||||||
seconds = remainder
|
|
||||||
```
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# Get Quotient And Remainder In One Operation
|
|
||||||
|
|
||||||
While writing some custom code to transform a number of seconds into the
|
|
||||||
constituent hours, minutes, and seconds, I found myself needing to get both the
|
|
||||||
quotient and remainder from a division between two numbers.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> import math
|
|
||||||
>>> math.floor(3666 / 3600)
|
|
||||||
1
|
|
||||||
>>> 3666 % 3600
|
|
||||||
66
|
|
||||||
```
|
|
||||||
|
|
||||||
Instead, I can use Python's built-in
|
|
||||||
[`divmod`](https://docs.python.org/3/library/functions.html#divmod) function to
|
|
||||||
compute both values in one statement.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> divmod(3666, 3600)
|
|
||||||
(1, 66)
|
|
||||||
```
|
|
||||||
|
|
||||||
The result is a tuple with the first value being my quotient (in this case, the
|
|
||||||
number of hours) and the remainder (the remaining number of seconds).
|
|
||||||
|
|
||||||
This kind of operation is known as [Euclidian
|
|
||||||
Division](https://en.wikipedia.org/wiki/Euclidean_division).
|
|
||||||
|
|
||||||
Here is a snippet of some actual code where I use this in
|
|
||||||
[`py-vmt`](https://github.com/jbranchaud/py-vmt/blob/b9eae8b258e9fd720cfa3bb63b601225df352051/src/py_vmt/time_helpers.py#L14-L16):
|
|
||||||
|
|
||||||
```python
|
|
||||||
def format_time_delta(diff: timedelta) -> str:
|
|
||||||
total_seconds = int(diff.total_seconds())
|
|
||||||
|
|
||||||
hours, remainder = divmod(total_seconds, 3600)
|
|
||||||
minutes, remainder = divmod(remainder, 60)
|
|
||||||
seconds = remainder
|
|
||||||
|
|
||||||
# ...
|
|
||||||
```
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
# Install With PIP For Specific Interpreter
|
|
||||||
|
|
||||||
The `pip` module can be invoked for any of its commands, such as install, using
|
|
||||||
a specific Python interpreter like so:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ python3 -m pip install black
|
|
||||||
```
|
|
||||||
|
|
||||||
This avoid ambiguity between the version of Python I am using and version of the
|
|
||||||
package manager I'm using.
|
|
||||||
|
|
||||||
Similarly if I need to upgrade `pip`, I can do the following:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ python3 -m pip install --upgrade pip
|
|
||||||
```
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Iterate First N Items From Enumerable
|
|
||||||
|
|
||||||
As I'm working through the 2nd chapter of [Build a Large Language Model (from
|
|
||||||
scratch)](https://still.visualmode.dev/blogmarks/227), I came across a code
|
|
||||||
example processing a dictionary of words. This example used a for loop to print
|
|
||||||
out each dictionary entry until an index of 50 was reached on then it did a
|
|
||||||
`break`.
|
|
||||||
|
|
||||||
This struck me as an odd way to grab and process N items from a list. I did some
|
|
||||||
searching and found `itertools` which provides
|
|
||||||
[`islice`](https://docs.python.org/3/library/itertools.html#itertools.islice).
|
|
||||||
|
|
||||||
```python
|
|
||||||
from itertools import islice
|
|
||||||
|
|
||||||
# preprocess words from a file into a word list
|
|
||||||
all_words = ... # not shown here
|
|
||||||
|
|
||||||
vocab = {token: integer for integer, token in enumerate(all_words)}
|
|
||||||
for item in islice(enumerate(vocab.items()), 50):
|
|
||||||
print(item)
|
|
||||||
```
|
|
||||||
|
|
||||||
The `islice` function is a better approach because the intention (to grab the
|
|
||||||
first 50 things) is encoded in the function call rather than buried in a loop
|
|
||||||
body. It also has equivalent memory efficiency to the original example because
|
|
||||||
it lazily processes the list of `vocab` items.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Iterate Over A Dictionary
|
|
||||||
|
|
||||||
Let's say we have a `dict` that contains counts of occurrences for each word in
|
|
||||||
some sample text:
|
|
||||||
|
|
||||||
```python
|
|
||||||
words_frequency = {
|
|
||||||
"the": 4,
|
|
||||||
"a": 3,
|
|
||||||
"dog": 1,
|
|
||||||
"bone": 1,
|
|
||||||
"wants": 1,
|
|
||||||
...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Here is how we can iterate over the `dict`, accessing both the keys and values:
|
|
||||||
|
|
||||||
```python
|
|
||||||
for word, count in word_frequency.items():
|
|
||||||
print(f"- {word} appears {count} time{'' if count == 1 else 's'}")
|
|
||||||
```
|
|
||||||
|
|
||||||
Using the
|
|
||||||
[`items()`](https://docs.python.org/3/library/stdtypes.html#dict.items) method,
|
|
||||||
we're able to access both _key_ and _value_ with the for loop as it iterates.
|
|
||||||
|
|
||||||
Another approach is to loop directly on the `dict` which implicitly surfaces the
|
|
||||||
_key_ for iteration. This can then be used to get the value from the `dict`:
|
|
||||||
|
|
||||||
```python
|
|
||||||
for word in word_frequency:
|
|
||||||
print(f"- {word}: {word_frequency[word]}
|
|
||||||
```
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
# Keep A Tally With collections.Counter
|
|
||||||
|
|
||||||
Python's `collections` module comes with a
|
|
||||||
[`Counter`](https://docs.python.org/3/library/collections.html#collections.Counter)
|
|
||||||
object which is a specialized dict subclass focussed on tallying counts of keys.
|
|
||||||
|
|
||||||
> It is a collection where elements are stored as dictionary keys and their
|
|
||||||
> counts are stored as dictionary values. Counts are allowed to be any integer
|
|
||||||
> value including zero or negative counts.
|
|
||||||
|
|
||||||
I used it recently while doing an exploratory implementation of a Byte-Pair
|
|
||||||
Encoding (BPE):
|
|
||||||
|
|
||||||
```python
|
|
||||||
from collections import Counter
|
|
||||||
|
|
||||||
def get_pair_counts(token_ids: list[int]) -> Counter:
|
|
||||||
"""Count how often each adjacent pair appears"""
|
|
||||||
counts = Counter()
|
|
||||||
for i in range(len(token_ids) - 1):
|
|
||||||
pair = (token_ids[i], token_ids[i + 1])
|
|
||||||
counts[pair] += 1
|
|
||||||
return counts
|
|
||||||
```
|
|
||||||
|
|
||||||
Here I'm able to count the number of occurrences of each pair of bytes from the
|
|
||||||
input text. A tuple of `int` values is hashable, so they work great as keys for
|
|
||||||
a `Counter`.
|
|
||||||
|
|
||||||
The count value of any key will default to `0`. That makes it straightforward to
|
|
||||||
increment from there as you iterating over occurrences.
|
|
||||||
|
|
||||||
```python
|
|
||||||
>>> counts = Counter()
|
|
||||||
>>> counts['hello']
|
|
||||||
0
|
|
||||||
>>> count['hello'] += 1
|
|
||||||
>>> count['hello']
|
|
||||||
1
|
|
||||||
```
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
# Lint And Format Project With Ruff
|
|
||||||
|
|
||||||
[Ruff](https://docs.astral.sh/ruff/) is "an extremely fast Python linter and
|
|
||||||
code formatter, written in Rust." I recently added it to my [`py-vmt` CLI project](https://github.com/jbranchaud/py-vmt) and here are some of the commands
|
|
||||||
I used right out of the box.
|
|
||||||
|
|
||||||
First, I use `uv` and so I installed it like so:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ uv add --dev ruff
|
|
||||||
```
|
|
||||||
|
|
||||||
First, I checked for linting errors. There were a bunch. The output looked like
|
|
||||||
this:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ uv run ruff check
|
|
||||||
F841 Local variable `frozen_datetime` is assigned to but never used
|
|
||||||
--> tests/src/py_vmt/test_cli.py:88:43
|
|
||||||
|
|
|
||||||
86 | 2026, 3, 14, 15, 5, 11, 0, datetime.timezone.utc
|
|
||||||
87 | )
|
|
||||||
88 | with freeze_time(initial_datetime) as frozen_datetime:
|
|
||||||
| ^^^^^^^^^^^^^^^
|
|
||||||
89 | # cancel session without one started
|
|
||||||
90 | cancel_result = runner.invoke(cli, ["cancel"])
|
|
||||||
|
|
|
||||||
help: Remove assignment to unused variable `frozen_datetime`
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
Found 11 errors.
|
|
||||||
[*] 2 fixable with the `--fix` option (7 hidden fixes can be enabled with the `--unsafe-fixes` option).
|
|
||||||
```
|
|
||||||
|
|
||||||
Some of them could be automatically fixed, so I dealt with those first using the
|
|
||||||
`--fix` flag.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ uv run ruff check --fix
|
|
||||||
```
|
|
||||||
|
|
||||||
The remaining lint issues I had to deal with manually. Once I had addressed all
|
|
||||||
of them I got this message:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ uv run ruff check
|
|
||||||
All checks passed!
|
|
||||||
```
|
|
||||||
|
|
||||||
With the lint issues out of the way, the next stuff was to use `ruff` to apply
|
|
||||||
consistent auto-formatting across the entire project. Because I hadn't been
|
|
||||||
using any auto-formatter up to this point on this project, I can expect the diff
|
|
||||||
to be significant.
|
|
||||||
|
|
||||||
I can start with a dry run using the `--check` flag. This gives a summary of how
|
|
||||||
much formatting churn there is going to be.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ uv run ruff format --check
|
|
||||||
Would reformat: src/py_vmt/cli.py
|
|
||||||
Would reformat: src/py_vmt/session.py
|
|
||||||
Would reformat: src/py_vmt/time_helpers.py
|
|
||||||
Would reformat: tests/src/py_vmt/test_cli.py
|
|
||||||
Would reformat: tests/src/py_vmt/test_session.py
|
|
||||||
Would reformat: tests/src/py_vmt/test_time_helpers.py
|
|
||||||
6 files would be reformatted, 3 files already formatted
|
|
||||||
```
|
|
||||||
|
|
||||||
I'm using git and I have a clean working copy, so there is no real harm in just
|
|
||||||
going for it either.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
❯ uv run ruff format
|
|
||||||
6 files reformatted, 3 files left unchanged
|
|
||||||
```
|
|
||||||
|
|
||||||
That makes all the formatting changes and I can use `git diff` to browse through
|
|
||||||
them before eventually committing them.
|
|
||||||
|
|
||||||
See `uv run ruff check --help` and `uv run ruff format --help` for more details.
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user