From 7baf38257df92565d65133d19a59b402261def0f Mon Sep 17 00:00:00 2001 From: weijing24 <645509024@qq.com> Date: Wed, 11 Mar 2026 22:19:05 +0800 Subject: [PATCH 1/3] feat: replace SVN with git sparse-checkout for snippet downloads GitHub sunset SVN support in January 2024, breaking the `svn` ice used to download subdirectories (e.g., OMZ plugins). This replaces the SVN implementation with git sparse-checkout which provides the same subdirectory cloning capability using native git. Changes: - ZI_1MAP: remove /trunk/ from URLs (no longer needed for git) - .zi-mirror-using-svn(): rewrite to use git clone --no-checkout --depth=1 --filter=tree:0 + git sparse-checkout - side.zsh: detect .git in addition to .svn for existing snippets - autoload.zsh: use git status when .git dir is present --- lib/zsh/autoload.zsh | 2 +- lib/zsh/install.zsh | 51 +++++++++++++++++++++++++++++--------------- lib/zsh/side.zsh | 4 ++-- zi.zsh | 12 +++++------ 4 files changed, 43 insertions(+), 26 deletions(-) diff --git a/lib/zsh/autoload.zsh b/lib/zsh/autoload.zsh index db32118e..deab3726 100755 --- a/lib/zsh/autoload.zsh +++ b/lib/zsh/autoload.zsh @@ -1613,7 +1613,7 @@ ZI[EXTENDED_GLOB]="" if [[ "$st" = "status" ]]; then if (( ${+ICE2[svn]} )); then builtin print -r -- "${ZI[col-info]}Status for ${${${local_dir:h}:t}##*--}/${local_dir:t}${ZI[col-rst]}" - ( builtin cd -q "$local_dir"; command svn status -vu ) + ( builtin cd -q "$local_dir"; if [[ -d .git ]]; then command git status; else command svn status -vu; fi ) retval=$? builtin print else diff --git a/lib/zsh/install.zsh b/lib/zsh/install.zsh index 8df2c07c..4cd761c9 100755 --- a/lib/zsh/install.zsh +++ b/lib/zsh/install.zsh @@ -709,10 +709,10 @@ builtin source "${ZI[BIN_DIR]}/lib/zsh/side.zsh" || { builtin print -P "${ZI[col return 0 } # ]]] # FUNCTION: .zi-mirror-using-svn [[[ -# Used to clone subdirectories from Github. -# If in update mode (see $2), then invokes `svn update', -# in normal mode invokes `svn checkout --non-interactive -q '. -# In test mode only compares remote and local revision and outputs true if update is needed. +# Used to clone subdirectories from Github using git sparse-checkout. +# If in update mode (see $2), then invokes `git pull', +# in normal mode invokes `git clone' with sparse-checkout. +# In test mode only compares remote and local HEAD and outputs true if update is needed. # # $1 - URL # $2 - mode, "" - normal, "-u" - update, "-t" - test @@ -721,28 +721,45 @@ builtin source "${ZI[BIN_DIR]}/lib/zsh/side.zsh" || { builtin print -P "${ZI[col builtin setopt localoptions extendedglob warncreateglobal local url="$1" update="$2" directory="$3" - (( ${+commands[svn]} )) || \ - +zi-message "{error}Warning{ehi}:{rst} Subversion not found{nl}{mmdsh}{rst} {auto}Please install it to use \`svn' ice" + (( ${+commands[git]} )) || \ + +zi-message "{error}Warning{ehi}:{rst} Git not found{nl}{mmdsh}{rst} {auto}Please install it to use \`svn' ice" + + # Parse GitHub URL to extract repo URL and subpath + # e.g. https://github.com/ohmyzsh/ohmyzsh/plugins/git + # -> repo_url=https://github.com/ohmyzsh/ohmyzsh subpath=plugins/git + local repo_url subpath + if [[ "$url" = *github.com* ]]; then + repo_url=${(M)url##https://github.com/[^/]##/[^/]##} + subpath=${url#$repo_url} + subpath=${subpath#/} + subpath=${subpath%/} + else + +zi-message "{error}Warning{ehi}:{rst} Non-GitHub URLs are not supported for \`svn' ice without Subversion" + return 1 + fi if [[ "$update" = "-t" ]]; then ( () { builtin setopt localoptions noautopushd; builtin cd -q "$directory"; } - local -a out1 out2 - out1=( "${(f@)"$(LANG=C svn info -r HEAD)"}" ) - out2=( "${(f@)"$(LANG=C svn info)"}" ) - - out1=( "${(M)out1[@]:#Revision:*}" ) - out2=( "${(M)out2[@]:#Revision:*}" ) - [[ "${out1[1]##[^0-9]##}" != "${out2[1]##[^0-9]##}" ]] && return 0 + local local_rev remote_rev + local_rev=$(command git rev-parse HEAD 2>/dev/null) + remote_rev=$(command git ls-remote --refs "$repo_url" HEAD 2>/dev/null | command awk '{print $1}') + [[ -n "$remote_rev" && "$local_rev" != "$remote_rev" ]] && return 0 return 1 ) return $? fi - if [[ "$update" = "-u" && -d "$directory" && -d "$directory/.svn" ]]; then + if [[ "$update" = "-u" && -d "$directory" && -d "$directory/.git" ]]; then ( () { builtin setopt localoptions noautopushd; builtin cd -q "$directory"; } - command svn update + command git pull --depth=1 -q origin 2>/dev/null return $? ) else - command svn checkout --non-interactive -q "$url" "$directory" + command git clone --no-checkout --depth=1 --filter=tree:0 -q "$repo_url" "$directory" || return 4 + ( () { builtin setopt localoptions noautopushd; builtin cd -q "$directory"; } || return 4 + if [[ -n "$subpath" ]]; then + command git sparse-checkout set "$subpath" 2>/dev/null + fi + command git checkout -q 2>/dev/null + return $? ) fi return $? } @@ -935,7 +952,7 @@ builtin source "${ZI[BIN_DIR]}/lib/zsh/side.zsh" || { builtin print -P "${ZI[col ( () { builtin setopt localoptions noautopushd; builtin cd -q "$local_dir"; } || return 4 (( !OPTS[opt_-q,--quiet] )) && \ - +zi-message "Downloading{ehi}:{rst} {apo}\`{url}$sname{apo}\`{rst}${${ICE[svn]+" ({p}with Subversion{rst})"}:-" ({p}with curl, wget, lftp{rst})"}{…}" + +zi-message "Downloading{ehi}:{rst} {apo}\`{url}$sname{apo}\`{rst}${${ICE[svn]+" ({p}with git sparse-checkout{rst})"}:-" ({p}with curl, wget, lftp{rst})"}{…}" if (( ${+ICE[svn]} )) { if [[ $update = -u ]] { diff --git a/lib/zsh/side.zsh b/lib/zsh/side.zsh index 3017c217..dd88e99b 100755 --- a/lib/zsh/side.zsh +++ b/lib/zsh/side.zsh @@ -126,8 +126,8 @@ .zi-get-object-path snippet "$url1" local_dirA=$reply[-3] dirnameA=$reply[-2] - [[ -d "$local_dirA/$dirnameA/.svn" ]] && { - svn_dirA=".svn" + [[ -d "$local_dirA/$dirnameA/.git" || -d "$local_dirA/$dirnameA/.svn" ]] && { + if [[ -d "$local_dirA/$dirnameA/.git" ]]; then svn_dirA=".git"; else svn_dirA=".svn"; fi if { .zi-first % "$local_dirA/$dirnameA"; } { fileB_there=( ${reply[-1]} ) } diff --git a/zi.zsh b/zi.zsh index 200a7492..dd900700 100755 --- a/zi.zsh +++ b/zi.zsh @@ -206,12 +206,12 @@ ZI[TMP_SUBST]=inactive ZI[DTRACE]=0 ZI[CUR_PLUGIN]= # Parameters - ICE. [[[ typeset -gA ZI_1MAP ZI_2MAP ZI_1MAP=( - OMZ:: https://github.com/ohmyzsh/ohmyzsh/trunk/ - OMZP:: https://github.com/ohmyzsh/ohmyzsh/trunk/plugins/ - OMZT:: https://github.com/ohmyzsh/ohmyzsh/trunk/themes/ - OMZL:: https://github.com/ohmyzsh/ohmyzsh/trunk/lib/ - PZT:: https://github.com/sorin-ionescu/prezto/trunk/ - PZTM:: https://github.com/sorin-ionescu/prezto/trunk/modules/ + OMZ:: https://github.com/ohmyzsh/ohmyzsh/ + OMZP:: https://github.com/ohmyzsh/ohmyzsh/plugins/ + OMZT:: https://github.com/ohmyzsh/ohmyzsh/themes/ + OMZL:: https://github.com/ohmyzsh/ohmyzsh/lib/ + PZT:: https://github.com/sorin-ionescu/prezto/ + PZTM:: https://github.com/sorin-ionescu/prezto/modules/ ) ZI_2MAP=( OMZ:: https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/ From f6500bd24be0701472fa719ba789b3542d99fa16 Mon Sep 17 00:00:00 2001 From: weijing24 <645509024@qq.com> Date: Wed, 11 Mar 2026 22:37:20 +0800 Subject: [PATCH 2/3] fix: clone to temp dir and copy subpath contents to target Git sparse-checkout preserves directory structure and cone mode checks out root files. Fix by cloning to temp dir with --no-cone, then copying only subpath contents to target directory. --- lib/zsh/install.zsh | 60 +++++++++++++++++++++++++++++---------------- lib/zsh/side.zsh | 4 +-- 2 files changed, 41 insertions(+), 23 deletions(-) diff --git a/lib/zsh/install.zsh b/lib/zsh/install.zsh index 4cd761c9..fc055251 100755 --- a/lib/zsh/install.zsh +++ b/lib/zsh/install.zsh @@ -739,29 +739,47 @@ builtin source "${ZI[BIN_DIR]}/lib/zsh/side.zsh" || { builtin print -P "${ZI[col fi if [[ "$update" = "-t" ]]; then - ( () { builtin setopt localoptions noautopushd; builtin cd -q "$directory"; } - local local_rev remote_rev - local_rev=$(command git rev-parse HEAD 2>/dev/null) - remote_rev=$(command git ls-remote --refs "$repo_url" HEAD 2>/dev/null | command awk '{print $1}') - [[ -n "$remote_rev" && "$local_rev" != "$remote_rev" ]] && return 0 - return 1 - ) - return $? + local local_rev remote_rev + local_rev="$(<"$directory/.zi_rev" 2>/dev/null)" + remote_rev=$(command git ls-remote "$repo_url" HEAD 2>/dev/null | command awk '{print $1}') + [[ -n "$remote_rev" && "$local_rev" != "$remote_rev" ]] && return 0 + return 1 fi - if [[ "$update" = "-u" && -d "$directory" && -d "$directory/.git" ]]; then - ( () { builtin setopt localoptions noautopushd; builtin cd -q "$directory"; } - command git pull --depth=1 -q origin 2>/dev/null - return $? ) - else - command git clone --no-checkout --depth=1 --filter=tree:0 -q "$repo_url" "$directory" || return 4 - ( () { builtin setopt localoptions noautopushd; builtin cd -q "$directory"; } || return 4 - if [[ -n "$subpath" ]]; then - command git sparse-checkout set "$subpath" 2>/dev/null + + # Clone to temp dir, sparse-checkout subpath, copy contents to target + local tmpdir + tmpdir=$(command mktemp -d) || return 4 + { + command git clone --no-checkout --depth=1 --filter=tree:0 -q "$repo_url" "$tmpdir/repo" || return 4 + ( () { builtin setopt localoptions noautopushd; builtin cd -q "$tmpdir/repo"; } || return 4 + if [[ -n "$subpath" ]]; then + command git sparse-checkout set --no-cone "/$subpath" 2>/dev/null + fi + command git checkout -q 2>/dev/null + ) || return 4 + + local src_dir="$tmpdir/repo" + [[ -n "$subpath" ]] && src_dir="$tmpdir/repo/$subpath" + + if [[ -d "$src_dir" ]]; then + command mkdir -p "$directory" + if [[ "$update" = "-u" ]]; then + # Remove old files except .zi metadata + command find "$directory" -maxdepth 1 -not -name '.zi' -not -name '.zi_rev' -not -path "$directory" -exec rm -rf {} + 2>/dev/null + fi + command cp -Rf "$src_dir"/* "$directory"/ 2>/dev/null + command cp -Rf "$src_dir"/.[^.]* "$directory"/ 2>/dev/null + # Store revision for update checks + local rev + rev=$(command git -C "$tmpdir/repo" rev-parse HEAD 2>/dev/null) + [[ -n "$rev" ]] && builtin print -r -- "$rev" > "$directory/.zi_rev" + else + return 4 fi - command git checkout -q 2>/dev/null - return $? ) - fi - return $? + } always { + command rm -rf "$tmpdir" + } + return 0 } # ]]] # FUNCTION: .zi-forget-completion [[[ diff --git a/lib/zsh/side.zsh b/lib/zsh/side.zsh index dd88e99b..a6c6b218 100755 --- a/lib/zsh/side.zsh +++ b/lib/zsh/side.zsh @@ -126,8 +126,8 @@ .zi-get-object-path snippet "$url1" local_dirA=$reply[-3] dirnameA=$reply[-2] - [[ -d "$local_dirA/$dirnameA/.git" || -d "$local_dirA/$dirnameA/.svn" ]] && { - if [[ -d "$local_dirA/$dirnameA/.git" ]]; then svn_dirA=".git"; else svn_dirA=".svn"; fi + [[ -f "$local_dirA/$dirnameA/.zi_rev" || -d "$local_dirA/$dirnameA/.svn" ]] && { + if [[ -f "$local_dirA/$dirnameA/.zi_rev" ]]; then svn_dirA=".zi_rev"; else svn_dirA=".svn"; fi if { .zi-first % "$local_dirA/$dirnameA"; } { fileB_there=( ${reply[-1]} ) } From 1b0b57bb4b2c984f959c4a50dfafa2958d68f05c Mon Sep 17 00:00:00 2001 From: weijing24 <645509024@qq.com> Date: Wed, 11 Mar 2026 22:43:32 +0800 Subject: [PATCH 3/3] fix: use (DN) glob qualifier to avoid no-match error on dotfiles copy --- lib/zsh/install.zsh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/zsh/install.zsh b/lib/zsh/install.zsh index fc055251..b93f1809 100755 --- a/lib/zsh/install.zsh +++ b/lib/zsh/install.zsh @@ -767,8 +767,7 @@ builtin source "${ZI[BIN_DIR]}/lib/zsh/side.zsh" || { builtin print -P "${ZI[col # Remove old files except .zi metadata command find "$directory" -maxdepth 1 -not -name '.zi' -not -name '.zi_rev' -not -path "$directory" -exec rm -rf {} + 2>/dev/null fi - command cp -Rf "$src_dir"/* "$directory"/ 2>/dev/null - command cp -Rf "$src_dir"/.[^.]* "$directory"/ 2>/dev/null + command cp -Rf "$src_dir"/(^.git)(DN) "$directory"/ 2>/dev/null # Store revision for update checks local rev rev=$(command git -C "$tmpdir/repo" rev-parse HEAD 2>/dev/null)