🔗 A nice summary of Git commits with files when rebasing
When you’re making a large interactive rebase, it’s useful to see what files each commit touches.
Here’s a nice trick. Take the list of commits from `git rebase -i` and save it into a text file, `commits.txt`, which should look something like this:
pick aef18c84 fix something pick efa1d6pd add a feature pick 74417d1d something else
then run this:
(cat commits.txt | while read x commit rest; do echo; echo "#" $commit $rest; git show $commit --pretty=format:'## %an' --name-only; done ) > commits.md
This will generate a file called `commits.md`, with contents like this:
# aef18c84 fix something ## Hisham Muhammad project/some/file project/another/file # efa1d6pd add a feature ## John Doe project/blah.txt project/another/file # 74417d1d something else ## Hisham Muhammad project/another/file something/else/entirely spec/01-test_another_file_spec.lua
Keeping this `commit.md` open side-by-side to an interactive rebase session makes it a lot easier to quickly glance which commits touch the same files, which greatly reduces the changes of conflicts when moving commits around. Plus, using hash signs and the .md extension makes the whole thing even easier to read, when your text editor has support for highlighting Markdown files!
🔗 Writing release announcement emails
Mailing lists are not exactly fashionable nowadays, but some of them remain relevant for some communities. The Lua community is one such example. As of 2017, a lot of what goes on in the Lua module development world still resonates in lua-l. With over 2500 subscribers, it’s a good way to kickstart interest in your new project.
Mailing list users tend to be somewhat pedantic about etiquette guidelines for posting, especially for announcements and the like. So, I usually follow this little formula for writing release announcement emails, which has been effective for me:
- Email subject - this is important; I use a format like “[ANN] MyProject x.y”
- Summary - The first paragraph explains what is the project
- Links and installation - Then a link to the project website, and a one-liner instruction of how to install it (that is, the incantation for the appropriate package manager — in the case of Lua,
luarocks install myproject
). More detailed instructions and documentation should be available from the project website. - Description - Finally, a more detailed description:
- If the announcement is for a new version of an existing project that was previously announced on the list, I include a summarized changelog, essentially “What’s new in version x.y:”
- If this is the first announcement of the project, then a longer description of how the project works. For Lua modules, for example, this may include a really short “hello-world”-type example for the library. This is information that should be in the README.md file for your repository, which in future announcements will be reachable via the link for the project website (often a Github repo URL) mentioned above.
- License - Users should be able to figure out the license of your project easily, so especially in new projects mentioning can be a good idea — but watch out if you’re using a license that’s not the majority option in a given community. You may be unnecessarily flamed for your choice by people who don’t even want to use your project in the first place. If you’re not going with the “majority license” (and remember, license choice is your call as an author, not the community’s) it might be a better idea to avoid mailing list noise and mention the license only in the project website and sources. The goal is not to hide it (interested people should find it easily; do mention it in your project’s README.md and include a LICENSE file) but just to avoid licensing flamewars. Of course, using the majority license has major pros, so if it’s all the same to you go with it, but if you’d prefer another one, don’t let yourself be bullied by a community into picking one free software license over another. It’s your freedom too!
- Be nice! - Finally, remember to sandwich all this technical info with greetings at the top, kudos to contributors, requests for help and feedback, etc. A mailing list is a social medium, after all. :)
An example of an upgrade announcement is here:
[ANN] LuaRocks 2.4.2
Hello, list! I'm happy to announce LuaRocks 2.4.2. LuaRocks is the Lua package manager. (For more information, please visit http://luarocks.org ) http://luarocks.org/releases/luarocks-2.4.2.tar.gz http://luarocks.org/releases/luarocks-2.4.2-win32.zip Those of you on Unix who are running LuaRocks as a rock (i.e. those who previously installed using `make bootstrap`) can install it using: luarocks install luarocks What's new since 2.4.1: * Fixed conflict resolution on deploy/delete * Improved dependency check messages * Performance improvements when removing packages * Support user-defined `platforms` array in config file * Improvements in Lua interpreter version detection in Unix configure script * Relaxed Lua version detection to improve support for alternative implementations (e.g. Ravi) * Plus assorted bugfixes and improvements This release contains commits by Peter Melnichenko, Robert Karasek and myself. As always, all kinds of feedback is greatly appreciated. Thank you, enjoy! -- Hisham
An example of a new project announcement is here:
[ANN] safer - Paranoid Lua programming
Hi, Announcing yet another "strict-mode" style module: "safer". * http://github.com/hishamhm/safer Install with luarocks install safer # Safer - Paranoid Lua programming Taking defensive programming to the next level. Use this module to avoid unexpected globals creeping up in your code, and stopping sub-modules from fiddling with fields of tables as you pass them around. ## API #### `safer.globals([exception_globals], [exception_nils])` No new globals after this point. `exception_globals` is an optional set (keys are strings, values are `true`) specifying names to be exceptionally accepted as new globals. Use this in case you have to declare a legacy module that declares a global, for example. A few legacy modules are already handled by default. `exception_nils` is an optional set (keys are strings, values are `true`) specifying names to be exceptionally accepted to be accessed as nonexisting globals. Use this in case code does feature-testing based on checking the presence of globals. A few common feature-test nils such as `jit` and `unpack` are already handled by default. #### `t = safer.table(t)` Block creation of new fields in this table. #### `t = safer.readonly(t)` Make table read-only: block creation of new fields in this table and setting new values to existing fields. Note that both `safer.table` and `safer.readonly` are implemented creating a proxy table, so: * Equality tests will fail: `safer.readonly(t) ~= t` * If anyone still has a reference to this table prior to creating the safer version, they can still mess with the unsafe table and affect the safe one. About ----- Licensed under the terms of the MIT License, the same as Lua. During its genesis, this module was called "safe", but I renamed it to "safer" to remind us that we are never fully safe. ;) -- Hisham http://hisham.hm/ - @hisham_hm
Hope this helps!
🔗 The danger of simple examples
When discussing language syntax, people often resort to small examples using simple variables like foo
or x
, almost like “meta-syntactic variables”, i.e., to make clear these tokens are outside of the syntax under discussion.
One dangerous side-effect, though, is that these variables are always short and sweet. And syntax that works well with short variables doesn’t always work as well in real-world situations where they have to deal with the rest of the language.
Case the first
Recently we were discussing multiple assignment style in the Lua mailing list. Someone suggested this:
local a, b, c, d = e, f, g, h
…which makes the assignments “more parallel” than a single line and avoid writing lots of local
s.
I think this a case where the over-simplified example is misleading.
With real-world looking variables, it would look more like
local cfg, constraints, module_name, initial_path = "default_config", {}, get_module_name(ctx), "/etc/myapp/default.config"
So yeah, It looks pretty with a, b, c
but in the real world with significant names, this becomes a pain to maintain, and when we stuff too much in a single line, diffs are harder to read.
Case the second
Things always look good in tiny examples with single-letter variables. Which brings me to a gripe I have with an often-suggested Lua idiom: the famous t[#t+1] = v
to append to arrays.
The reason why I think it’s so disengenious to defend t[#t+1] = v
as the preferred idiom for appending to an array is because it looks good with a single-letter variable and five-line tutorial examples, but in the real world we use nested tables. In the end, table.insert(my.nested[data], v)
is both more readable and avoids repetition:
- it’s one thing less to have a typo on
- one thing less to change
- and one thing less to get out of sync.
Note how it’s not even necessarily shorter: in this realistic example the variable name dominates the size of the statement:
table.insert(my.nested[data], val) my.nested[data][#my.nested[data] + 1] = val
Do I think table.insert
is too long? Yes I do, I wouldn’t mind having a shorter idiom (many were proposed in the Lua list over the years, most of them were fine, but I’m not getting into them because we risk delving into syntactic bikeshedding again, so let’s avoid that).
Do I think it’s worth it to add local tinsert = table.insert
to every program? No, I think this is worse than the t[#t+1] = v
idiom, because I hate having to guess which abbreviation the module author used to write a shorter table.insert
in their code (I’ve even seen local append = table.insert
in the wild!). And then again, the abbreviation doesn’t gain us much: being comfortable to read is more important than being comfortable to write, but being easy to maintain is just as important if not more.
And yes, it is important to ponder what are the differences between being “easy to read”, “easy to write” and “easy to maintain”. And when pondering those, watch out for misleading short variables in the examples!
Of course, some idioms are advisable specifically for when you have short variables:
local r, g, b = 0, 255, 0
Everyone can easily read what’s going on there. But note that, almost without noticing, I also used a realistic example here! Realistic examples help getting the discussion grounded, and I find that they are often lacking when discussing syntax.
PS: And before someone mentions, the performance gains for localizing such variables as local tinsert = table.insert
are overstated:
- When done at the top of modules they become upvalues and not true locals;
- Most of the modules I’ve seen doing this are far from being aimed at performance-intensive tasks that would warrant this kind of micro-optimization;
- Optimization advice changes between Lua implementations; while the cached local helps for interpreted Lua, it may actually hurt for LuaJIT. So just aim for clearer code. If you have an optimization problem you can measure and the local variable does bring a benefit, do it in a small scope, close to your performance-sensitive tight loop so that whoever is reading your code can understand what is going on.
🔗 Pen-and-paper Street Fighter II
I just remembered an interesting tidbit from my childhood.
Around 7th grade in school I invented a pen-and-paper version of Street Fighter II for people to play during classes.
I don’t remember the exact details, but basically I drew a grid for the screen and then I drew stick figures in it, and passed the page around.
People would write-in their moves and then I played CPU: I’d erase the stick figures and redraw in new positions, update hit/miss, update the energy meters.
I remember trying to keep it balanced and true to the game: Dhalsim’s punch and kick could hit farther but were weaker, etc. I had all of the “sprites” with the character movements pre-determined on my notebook.
The game went on sneaking a page back and forth along players and me at the back of the class. I imagine how bored out of our minds we must have been in school to enjoy playing “Street Fighter II at 0.05 frames per second”.
🔗 Fun hack to redirect stdout and stderr in order
Prologue
This is anecdote about roundabout ways to get stuff done. Pierre mentioned in the comments below that a proper way to solve this is to use unbuffer (though it does _not_ produce the exact same order as the terminal!). But if you want to read the improper way to do this, read on! :)
The story
Due to buffering, the terminal messes with the order of stdout and stderr of a program when redirecting to a file or another program. It prints the outputs of both descriptors in correct order relative to each other when printing straight to the terminal:
] ./my_program stdout line 1 stdout line 2 stderr line 1 stdout line 3 stderr line 2 stderr line 3
This doesn’t change the order:
] ./my_program 2>&1 stdout line 1 stdout line 2 stderr line 1 stdout line 3 stderr line 2 stderr line 3
but it changes the order when saving to a file or redirecting to any program:
] ./my_program 2>&1 | cat stderr line 1 stderr line 2 stderr line 3 stdout line 1 stdout line 2 stdout line 3
This behavior is the same in three shells I tested (bash, zsh, dash).
A weird “solution”
I wanted to save the log while preserving the order of events. So I ended up with this evil hack:
] strace -ewrite -o trace.txt -s 2048 ./my_program; sed 's,^[^"]*"\(.*\)"[^"]*$,\1,g;s,\\n,,g;' trace.txt > mytrace.txt ] cat mytrace.txt stdout line 1 stdout line 2 stderr line 1 stdout line 3 stderr line 2 stderr line 3 +++ exited with 0 +++
It turns out that strace does log each write in the correct order, so I’m catching the write syscall.
Note the limitations: it truncates lines to 2048 characters (good enough for my logs) and I was simply cutting off n and not cleaning up any other escape characters. But it worked well enough so I could read my ordered logs in a text editor!
Follow
🐘 Mastodon ▪ RSS (English), RSS (português), RSS (todos / all)
Last 10 entries
- Why I no longer say "conservative" when I mean "cautious"
- Sorting "git branch" with most recent branches last
- Frustrating Software
- What every programmer should know about what every programmer should know
- A degradação da web em tempos de IA não é acidental
- There are two very different things called "package managers"
- Last day at Kong
- A Special Hand
- How to change the nmtui background color
- Receita de Best Pancakes