Fix three bugs in .zi-check-for-git-changes() that broke zinit self-update#762
Conversation
|
@pschmitt and @vladdoster This seems a huge bug, and I am not sure how it went unnoticed so far. Essentially the self-update mechanism of I think I fixed properly; I prefer to wait someone of you taking a look into. In principle I should now be able to do merges of PR myself, but as I am new to this community I prefer to be careful. PS: There are also errors thrown by the test units. I investigated those and I think they have nothing to do with my changes. I could in the future look into this and repair the test units if it is broken (as I suspect; but would need extra work to understand the mechanics). |
vladdoster
left a comment
There was a problem hiding this comment.
looks good other than my one change suggestion
The gh-r tests are most likely transient errors. I'll take a look/fix. |
There was a problem hiding this comment.
Users would expect -q and --quiet flags.
Let's refactor this to use the current zinit command/option parser.
This has following benefits we get for free:
- self-update command usage via
-h/--help - global -q variable removing need to pass
-qaround and validate it
-attempt to follow current command convention
I can get it done today when I am free or I'm happy to help if you hit any issues with the refactor.
Refactor would be:
-
Add it here as well
-
update your code and check for quiet flag via
(( !OPTS[opt_-q,--quiet] )) -
Update completion for
self-updateto have help and quiet flags like cclear
|
LOL, I've had a PR open for 2 years addressing most of the |
…-parse-opts; extended number of supports variants, options Follows suggestions from zdharma-continuum#762 (review) Co-authored-by: vladislav doster <mvdoster@gmail.com>
@vladdoster Thanks! That was a very good idea. It makes the code much cleaner and easier to maintain in the future. I implemented all changes into two separate commits: 726f94f and 85a0721.
I think we are done. Do you want to take a final look? |
|
One last comment on the cosmetic. What are the guidelines? For example, we are still using print magic "%F{33}" for the colors in ```zsh |
|
I realized there are too many occurrences of |
…-parse-opts; extended number of supports variants, options Follows suggestions from zdharma-continuum#762 (review) Co-authored-by: vladislav doster <mvdoster@gmail.com>
|
@vladdoster You have an open "requested changes" flag. Is it the old one you originally opened or a new one? Shall we proceed with the merge? @pschmitt @alichtman Do you also want to take a look? You were all flagged for review (probably by @vladdoster). PS: Do we have a policy in this community on how to handle PR? How many reviewers do we typically expect/need, etc? Most likely it also depends on the extent and scope of the PR, but I was wondering whether we have general guidelines. |
|
Yeah it's been very yolo and random. |
|
Thanks, Philipp. I just wait a couple of days to see whether Vlad will also agree. He was much more involved, already initially with a first PR. |
vladdoster
left a comment
There was a problem hiding this comment.
Test flags to avoid regression
c27dc10 to
93e4182
Compare
|
@vladdoster I added the tests. Everything is now green. Do you want to check and merge? |
| if (( $@[(I)-*] || OPTS[opt_-h,--help] )); then | ||
| .zinit-parse-opts "$cmd" "$@" | ||
| if (( OPTS[opt_-h,--help] )); then | ||
| +zinit-prehelp-usage-message $cmd $___opt_map[$cmd] $@ | ||
| return 1; | ||
| elif (( ${reply[(I)-*]} )); then | ||
| +zinit-prehelp-usage-message $cmd $___opt_map[$cmd] ${reply[@]} | ||
| return 1 | ||
| fi | ||
| fi |
There was a problem hiding this comment.
I think this can be refactored to be ... Idk why it checked twice
| if (( $@[(I)-*] || OPTS[opt_-h,--help] )); then | |
| .zinit-parse-opts "$cmd" "$@" | |
| if (( OPTS[opt_-h,--help] )); then | |
| +zinit-prehelp-usage-message $cmd $___opt_map[$cmd] $@ | |
| return 1; | |
| elif (( ${reply[(I)-*]} )); then | |
| +zinit-prehelp-usage-message $cmd $___opt_map[$cmd] ${reply[@]} | |
| return 1 | |
| fi | |
| fi | |
| .zinit-parse-opts "$cmd" "$@" | |
| if (( OPTS[opt_-h,--help] )) || (( ${reply[(I)-*]} )); then | |
| +zinit-prehelp-usage-message $cmd $___opt_map[$cmd] ${reply[@]} | |
| return 1; | |
| fi |
|
My code before introduced (93e4182): They were perfectly readable. Each test has a self-describing name. Each assertion is explicit. The unknown-flags test uses two simple contains checks — easy to understand at a glance. But then you insulted them as cruft? I would invite to use a more polite tone. After your 4 commits: I feel these changes are a regress for the reasons:
On 0021ab4 (removing .zinit-parse-opts "self-update" "$@" from the case block): technically safe because the pre-guard at zinit.zsh:2632–2643 already calls it when any -* flags are present. But removing it makes the case block less self-contained, and the correctness now depends on understanding that earlier guard, which is non-obvious. My assessment: overall, these changes make the code less readable, the tests less useful, and the history noisier, and for no gain. I am sure you did not mean to be rude, and your changes were well intended. However, I would recommend reverting them or, at least in the future, avoiding such unnecessary chores if we want to keep a positive cooperation atmosphere. |
0021ab4 to
ebb9624
Compare
…nges
Bug description before the fix:
.zi-check-for-git-changes() checks if there are upstream changes by comparing HEAD...@{u}, but it does this without fetching first. It compares the local HEAD
against the locally cached upstream ref, which is stale until someone runs git fetch.
The flow:
1. .zi-check-for-git-changes runs git rev-list --left-right --count HEAD...@{u} — but @{u} refers to the local tracking ref (e.g.,
refs/remotes/origin/integrated), which hasn't been updated yet
2. Since no fetch has happened, the local tracking ref matches HEAD, so down is 0
3. The function prints "Already up-to-date." and returns 1 (no changes)
4. .zinit-self-update never enters the if block, so the git fetch + git pull on lines 2002-2024 never execute
The git fetch that would update the remote tracking refs is inside the block guarded by .zi-check-for-git-changes, so it could not be executed.
…ead of `git --work-tree <path>`
Co-authored-by: vladislav doster <mvdoster@gmail.com>
ebb9624 to
2bc6aa3
Compare
…-parse-opts; extended number of supports variants, options Follows suggestions from zdharma-continuum#762 (review) Co-authored-by: vladislav doster <mvdoster@gmail.com>
…ed log message Grammar correction suggested by @vladdoster.
- Add tests for `self-update` with `--help` and `--quiet` flags - Update expected output with no flags
…rguments are provided.
…e block - Removed since [`.zinit-parse-opts` is ran earlier](https://github.com/zdharma-continuum/zinit/pull/762/changes#diff-475a2f1e35813d9641ea77e854b9bf0d8c36b077bedbb6ae0bde1a0529dbbf0cR2634) in `zinit`
99a14d2 to
91e8e6a
Compare
|
I have rebased the branch onto main, creating a clean linear history without unnecessary merge commits that had accumulated. This PR fixes a long-standing bug that prevented If there are any remaining style or refactoring preferences, those can be addressed in a follow-up PR. |
This PR fixes a long-standing bug that prevented zinit self-update from ever detecting upstream changes, meaning every user who ran it would silently get "Already up to date." regardless of reality. I find it a priority to get this merged.
If there are any remaining style or refactoring preferences, those can be addressed in a follow-up PR.
Summary
Fix three bugs in
zinit self-update.Bug 1: No fetch before change detection
.zi-check-for-git-changes()comparedHEADagainst the local tracking ref (@{u}) without fetching first, so the tracking ref was always stale. Thegit fetchthat would have updated it lived inside.zinit-self-update(), in the block guarded by that very check — a chicken-and-egg problem.Result:
zinit self-updatealways reported "Already up-to-date." even when upstream had new commits.Bug 2:
git --work-treeinstead ofgit -C.zi-check-for-git-changes()usedgit --work-tree "$1"for all its git commands. This flag overrides where git looks for tracked files, but does not change where git discovers the.gitdirectory — that still happens by searching upward from the CWD. The correct flag isgit -C "$1", which changes directory first so that git finds the right.gitand operates on the right repository.The original author likely confused
--work-treewith-C, interpreting it as "run git in this directory." In reality,--work-treeis meant for setups where the.gitdirectory and working tree are in separate locations (e.g., bare repos with external worktrees, or dotfile management patterns). That's not the case for zinit, which is a standard clone.Result: If the user's CWD was inside a different git repo,
rev-listran against the wrong repository, returning bogus commit counts and triggering unnecessary recompile/reload.Bug 3:
-qflag never workedTwo separate issues prevented quiet mode from working:
Dispatcher didn't forward arguments. In
zinit.zsh, the dispatcher for theself-updatesubcommand called.zinit-self-updatewithout forwarding arguments:(self-update) .zinit-self-update ;;Other dispatcher entries (e.g.,
times) use"${@[2-correct,-1]}"to forward arguments. Because-qwas never passed through,.zinit-self-updatealways ran in verbose mode — all the[[ $1 != -q ]]checks always evaluated to true. (The only caller that actually passed-qwas the internal.zinit-self-update -qcall on line 3405.).zi-check-for-git-changes()didn't accept-q. Even if the dispatcher had forwarded-q, the log messages emitted by.zi-check-for-git-changes()(the "fetching latest changes" and "Already up-to-date." messages) had no quiet mode support.Result:
zinit self-update -qbehaved identically tozinit self-update.Why bugs 2 and 3 went unnoticed
Bug 1 masked bug 2. Without a fetch, the local
@{u}tracking ref was always equal toHEAD, so therev-listcomparison always returneddown=0. The function always took the "no changes" exit path — making the--work-treebug impossible to observe. Fixing the fetch (bug 1) made therev-listcomparison meaningful for the first time, which exposed bug 2.Bug 3 went unnoticed because
.zi-check-for-git-changes()never entered the code path where verbosity mattered — it always short-circuited with "Already up-to-date." due to bug 1. There was no visible difference between quiet and verbose when the function did nothing.Changes
git --work-tree "$1"withgit -C "$1"in.zi-check-for-git-changes()so git operates on the correct repository regardless of CWDgit fetchinto.zi-check-for-git-changes()so remote tracking refs are up-to-date before comparing.zi-check-for-git-changes()resolve the current branch name and return it via$REPLY, avoiding a duplicategit rev-parsecall in.zinit-self-update().zi-check-for-git-changes()alongside the fetchgit fetchand log line from the.zinit-self-update()subshell.zinit-self-updateinzinit.zshusing"${@[2-correct,-1]}", matching the pattern used by other subcommandsWhat was tested
zinit self-updatefrom a different git repo directory when there are no upstream changes — reports "Already up-to-date." without recompilingzinit self-updatewhen upstream has new commits — fetches, display changelog, pull, and recompilezinit self-updateagain immediately — reports "Already up-to-date."zinit self-update -q— quiet mode still works correctly