From 8bbc0f7825257c6531fc918169f232d5d670106b Mon Sep 17 00:00:00 2001 From: "RingOfStorms (Joshua Bell)" Date: Wed, 10 Sep 2025 16:05:17 -0500 Subject: [PATCH 1/4] new branching strategy --- common/general/shell/common.nix | 1 - common/general/shell/common.sh | 85 +++++++++++++++++++++++++++++++-- hosts/lio/flake.lock | 43 +++++------------ hosts/lio/flake.nix | 7 ++- 4 files changed, 95 insertions(+), 41 deletions(-) diff --git a/common/general/shell/common.nix b/common/general/shell/common.nix index 99d414a..b78ebe4 100644 --- a/common/general/shell/common.nix +++ b/common/general/shell/common.nix @@ -46,7 +46,6 @@ with lib; cl = "clear"; # git - branch = "git checkout -b"; status = "git status"; diff = "git diff"; branches = "git branch -a"; diff --git a/common/general/shell/common.sh b/common/general/shell/common.sh index 3f96c88..00b5d3d 100644 --- a/common/general/shell/common.sh +++ b/common/general/shell/common.sh @@ -129,12 +129,89 @@ prunel () { done; } -checkout () { - git fetch - git checkout $1 - pull +branch() { + local branch_name + branch_name=$1 + if [ -z "$branch_name" ]; then + echo "Usage: branch " >&2 + return 1 + fi + + # Use XDG_DATA_HOME or default to ~/.local/share + local xdg=${XDG_DATA_HOME:-$HOME/.local/share} + local repo_dir + # Determine the git common dir (points into the main repo's .git) + local common_dir + common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || { + echo "Not inside a git repository." >&2 + return 1 + } + # Make common_dir absolute if it's relative + if [ "${common_dir#/}" = "$common_dir" ]; then + common_dir="$(pwd)/$common_dir" + fi + # repo_dir is the path before '/.git' in the common_dir (handles worktrees) + repo_dir="${common_dir%%/.git*}" + if [ -z "$repo_dir" ]; then + echo "Unable to determine repository root." >&2 + return 1 + fi + + local repo_base + repo_base=$(basename "$repo_dir") + local repo_hash + repo_hash=$(printf "%s" "$repo_dir" | sha1sum | awk '{print $1}') + + # If user asked for default or master, cd back to repo root on default branch + local default_branch + default_branch=$(getdefault 2>/dev/null || echo "") + if [ "$branch_name" = "default" ] || [ "$branch_name" = "master" ] || [ "$branch_name" = "$default_branch" ]; then + cd "$repo_dir" || return 0 + # git fetch + # git checkout "$default_branch" + # pull + return 0 + fi + + # Ensure we have up-to-date remote info + git fetch --all --prune + + # If branch exists remotely and not locally, create local branch tracking remote + if git ls-remote --exit-code --heads origin "$branch_name" >/dev/null 2>&1; then + if ! git show-ref --verify --quiet "refs/heads/$branch_name"; then + git branch --track "$branch_name" "origin/$branch_name" 2>/dev/null || git branch "$branch_name" "origin/$branch_name" + fi + fi + + # Worktree path + local wt_root + wt_root="$xdg/git_worktrees/${repo_base}_${repo_hash}" + local wt_path + wt_path="$wt_root/$branch_name" + + mkdir -p "$wt_root" + + # If worktree already exists, cd to it + if [ -d "$wt_path/.git" ] || [ -d "$wt_path" -a -d "$wt_path/.git" ]; then + cd "$wt_path" || return 0 + return 0 + fi + + # If a worktree for this branch is already registered elsewhere, find it and cd + local existing + existing=$(git worktree list --porcelain 2>/dev/null | awk -v b="$branch_name" 'BEGIN{RS=""} $0 ~ "refs/heads/"b{for(i=1;i<=NF;i++) if ($i ~ /^worktree/) print $2 }') + if [ -n "$existing" ]; then + cd "$existing" || return 0 + return 0 + fi + + # Create the worktree + mkdir -p "$wt_path" + git worktree add -B "$branch_name" "$wt_path" "origin/$branch_name" 2>/dev/null || git worktree add "$wt_path" "$branch_name" + cd "$wt_path" || return 0 } + from_master () { git checkout $(getdefault) $@ } diff --git a/hosts/lio/flake.lock b/hosts/lio/flake.lock index cf35977..dc4f61e 100644 --- a/hosts/lio/flake.lock +++ b/hosts/lio/flake.lock @@ -29,22 +29,17 @@ "inputs": { "home-manager": "home-manager", "nix-flatpak": "nix-flatpak", - "nixpkgs": "nixpkgs_2", "ragenix": "ragenix" }, "locked": { - "lastModified": 1757006046, - "narHash": "sha256-DSPEc465ijE7hVDFpuMDF0LTBIAa0N7V6YFdBfS9XL0=", - "ref": "refs/heads/master", - "rev": "307bf34c8c24a7b2b599bd22facae5ec5090c8bd", - "revCount": 639, - "type": "git", - "url": "https://git.joshuabell.xyz/ringofstorms/dotfiles" + "path": "../../common", + "type": "path" }, "original": { - "type": "git", - "url": "https://git.joshuabell.xyz/ringofstorms/dotfiles" - } + "path": "../../common", + "type": "path" + }, + "parent": [] }, "crane": { "locked": { @@ -194,22 +189,6 @@ } }, "nixpkgs_2": { - "locked": { - "lastModified": 1757347588, - "narHash": "sha256-tLdkkC6XnsY9EOZW9TlpesTclELy8W7lL2ClL+nma8o=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "b599843bad24621dcaa5ab60dac98f9b0eb1cabe", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_3": { "locked": { "lastModified": 1741379970, "narHash": "sha256-Wh7esNh7G24qYleLvgOSY/7HlDUzWaL/n4qzlBePpiw=", @@ -225,7 +204,7 @@ "type": "github" } }, - "nixpkgs_4": { + "nixpkgs_3": { "locked": { "lastModified": 1755471983, "narHash": "sha256-axUoWcm4cNQ36jOlnkD9D40LTfSQgk8ExfHSRm3rTtg=", @@ -241,7 +220,7 @@ "type": "github" } }, - "nixpkgs_5": { + "nixpkgs_4": { "locked": { "lastModified": 1755648324, "narHash": "sha256-+2TxwJEXWXGC7JBsRGUHtmQ66lRGPcDI2kFKTTU5e2s=", @@ -1141,7 +1120,7 @@ "agenix": "agenix", "crane": "crane", "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs_3", + "nixpkgs": "nixpkgs_2", "rust-overlay": "rust-overlay" }, "locked": { @@ -1161,14 +1140,14 @@ "root": { "inputs": { "common": "common", - "nixpkgs": "nixpkgs_4", + "nixpkgs": "nixpkgs_3", "nixpkgs-unstable": "nixpkgs-unstable", "ros_neovim": "ros_neovim" } }, "ros_neovim": { "inputs": { - "nixpkgs": "nixpkgs_5", + "nixpkgs": "nixpkgs_4", "nvim_plugin-Almo7aya/openingh.nvim": "nvim_plugin-Almo7aya/openingh.nvim", "nvim_plugin-CopilotC-Nvim/CopilotChat.nvim": "nvim_plugin-CopilotC-Nvim/CopilotChat.nvim", "nvim_plugin-JoosepAlviste/nvim-ts-context-commentstring": "nvim_plugin-JoosepAlviste/nvim-ts-context-commentstring", diff --git a/hosts/lio/flake.nix b/hosts/lio/flake.nix index 4dabb81..0449c66 100644 --- a/hosts/lio/flake.nix +++ b/hosts/lio/flake.nix @@ -4,8 +4,8 @@ nixpkgs-unstable.url = "github:nixos/nixpkgs/nixos-unstable"; # Use relative to get current version for testing - # common.url = "path:../../common"; - common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles"; + common.url = "path:../../common"; + # common.url = "git+https://git.joshuabell.xyz/ringofstorms/dotfiles"; ros_neovim.url = "git+https://git.joshuabell.xyz/ringofstorms/nvim"; }; @@ -75,6 +75,7 @@ appimage-run nodejs_24 foot + vlc ]; # Also allow this key to work for root user, this will let us use this as a remote builder easier users.users.root.openssh.authorizedKeys.keys = [ @@ -117,11 +118,9 @@ "dev.vencord.Vesktop" "md.obsidian.Obsidian" "com.spotify.Client" - "org.videolan.VLC" "com.bitwarden.desktop" "org.openscad.OpenSCAD" "org.blender.Blender" - "im.riot.Riot" "com.rustdesk.RustDesk" "com.google.Chrome" ]; From 752310e3862bf02e74df3ea5d83af3a6bd464a2a Mon Sep 17 00:00:00 2001 From: "RingOfStorms (Joshua Bell)" Date: Wed, 10 Sep 2025 18:25:37 -0500 Subject: [PATCH 2/4] shell: add worktree branch helpers and link_ignored script; expose branch/branchd aliases --- common/general/shell/branch.sh | 113 +++++++++++++++++ common/general/shell/link_ignored.sh | 175 +++++++++++++++++++++++++++ 2 files changed, 288 insertions(+) create mode 100644 common/general/shell/branch.sh create mode 100755 common/general/shell/link_ignored.sh diff --git a/common/general/shell/branch.sh b/common/general/shell/branch.sh new file mode 100644 index 0000000..a8f6807 --- /dev/null +++ b/common/general/shell/branch.sh @@ -0,0 +1,113 @@ +# Branch and branchd helpers (worktree-based) + +# branch — create or jump to a worktree for +branch() { + # Use XDG_DATA_HOME or default to ~/.local/share + local xdg=${XDG_DATA_HOME:-$HOME/.local/share} + # Determine the git common dir (points into the main repo's .git) + local common_dir + common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || { + echo "Not inside a git repository." >&2 + return 1 + } + # Make common_dir absolute if it's relative + if [ "${common_dir#/}" = "$common_dir" ]; then + common_dir="$(pwd)/$common_dir" + fi + # repo_dir is the path before '/.git' in the common_dir (handles worktrees) + local repo_dir + repo_dir="${common_dir%%/.git*}" + if [ -z "$repo_dir" ]; then + echo "Unable to determine repository root." >&2 + return 1 + fi + + local repo_base + repo_base=$(basename "$repo_dir") + local repo_hash + repo_hash=$(printf "%s" "$repo_dir" | sha1sum | awk '{print $1}') + + local branch_name + branch_name=$1 + if [ -z "$branch_name" ]; then + echo "Usage: branch " >&2 + return 1 + fi + + # If user asked for default or master, cd back to repo root on default branch + local default_branch + default_branch=$(getdefault 2>/dev/null || echo "") + if [ "$branch_name" = "default" ] || [ "$branch_name" = "master" ] || [ "$branch_name" = "$default_branch" ]; then + cd "$repo_dir" || return 0 + git fetch + git checkout "$default_branch" + pull + return 0 + fi + + # Ensure we have up-to-date remote info + git fetch --all --prune + + # If branch exists remotely and not locally, create local branch tracking remote + if git ls-remote --exit-code --heads origin "$branch_name" >/dev/null 2>&1; then + if ! git show-ref --verify --quiet "refs/heads/$branch_name"; then + git branch --track "$branch_name" "origin/$branch_name" 2>/dev/null || git branch "$branch_name" "origin/$branch_name" + fi + fi + + # Worktree path + local wt_root + wt_root="$xdg/git_worktrees/${repo_base}_${repo_hash}" + local wt_path + wt_path="$wt_root/$branch_name" + + mkdir -p "$wt_root" + + # If worktree already exists at our expected path, cd to it + if [ -d "$wt_path/.git" ]; then + cd "$wt_path" || return 0 + return 0 + fi + + # If a worktree for this branch is already registered elsewhere, find it and cd + local existing + existing=$(git worktree list --porcelain 2>/dev/null | awk -v b="$branch_name" 'BEGIN{RS=""} $0 ~ "refs/heads/"b{for(i=1;i<=NF;i++) if ($i ~ /^worktree/) print $2 }') + if [ -n "$existing" ]; then + cd "$existing" || return 0 + return 0 + fi + + # Create the worktree + mkdir -p "$wt_path" + git worktree add -B "$branch_name" "$wt_path" "origin/$branch_name" 2>/dev/null || git worktree add "$wt_path" "$branch_name" + cd "$wt_path" || return 0 +} + +# branchd — remove current branch worktree +branchd() { + local current + current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || { + echo "Not inside a git repository." >&2 + return 1 + } + local default_branch + default_branch=$(getdefault 2>/dev/null || echo "") + + if [ "$current" = "$default_branch" ] || [ "$current" = "default" ]; then + echo "Already on default branch ($default_branch). Won't remove." >&2 + return 1 + fi + + # Find the worktree path for the current branch + local wt_path + wt_path=$(git worktree list --porcelain 2>/dev/null | awk -v b="$current" 'BEGIN{RS="";FS="\n"} $0 ~ "refs/heads/"b{for(i=1;i<=NF;i++) if ($i ~ /^worktree /) { sub(/^worktree /,"",$i); print $i }}') + if [ -z "$wt_path" ]; then + echo "Worktree for branch '$current' not found." >&2 + return 1 + fi + + # Switch to default branch (uses branch() helper) and then remove worktree + branch default || { echo "Failed to switch to default branch" >&2; return 1; } + + git worktree remove "$wt_path" +} diff --git a/common/general/shell/link_ignored.sh b/common/general/shell/link_ignored.sh new file mode 100755 index 0000000..95c0a71 --- /dev/null +++ b/common/general/shell/link_ignored.sh @@ -0,0 +1,175 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + cat </dev/null); then + echo "Error: not in a git repository." >&2 + exit 2 +fi +# Make absolute if relative +if [ "${common_dir#/}" = "$common_dir" ]; then + common_dir="$(pwd)/$common_dir" +fi +repo_root="${common_dir%%/.git*}" +if [ -z "$repo_root" ]; then + echo "Error: unable to determine repository root." >&2 + exit 2 +fi + +# Get list of untracked/ignored files from the repo root (relative paths) +mapfile -d $'\0' -t candidates < <(git -C "$repo_root" ls-files --others --ignored --exclude-standard -z || true) + +if [ ${#candidates[@]} -eq 0 ]; then + echo "No untracked/ignored files found in $repo_root" + exit 0 +fi + +# Collapse to top-level (first path component) and make unique. +# This prevents listing every file under node_modules/ or build/. +declare -A _seen +tops=() +for c in "${candidates[@]}"; do + # remove trailing slash if present + c="${c%/}" + top="${c%%/*}" + [ -z "$top" ] && continue + if [ -z "${_seen[$top]:-}" ]; then + _seen[$top]=1 + tops+=("$top") + fi +done + +if [ ${#tops[@]} -eq 0 ]; then + echo "No top-level ignored/untracked entries found in $repo_root" + exit 0 +fi + +# Filter top-level entries by provided patterns (if any) +if [ ${#PATTERNS[@]} -gt 0 ]; then + filtered=() + for t in "${tops[@]}"; do + for p in "${PATTERNS[@]}"; do + if [[ "$t" == *"$p"* ]]; then + filtered+=("$t") + break + fi + done + done +else + filtered=("${tops[@]}") +fi + +if [ ${#filtered[@]} -eq 0 ]; then + echo "No candidates match the provided patterns." >&2 + exit 0 +fi + +# Present selection +if command -v fzf >/dev/null 2>&1 && [ "$USE_FZF" -eq 1 ]; then + # Show preview of the source file (if text) and allow multi-select + selected=$(printf "%s\n" "${filtered[@]}" | fzf --multi --height=40% --border --prompt="Select files to link: " --preview "if [ -f '$repo_root'/{} ]; then bat --color always --paging=never --style=plain '$repo_root'/{}; else ls -la '$repo_root'/{}; fi") + if [ -z "$selected" ]; then + echo "No files selected." && exit 0 + fi + # Convert to array + IFS=$'\n' read -r -d '' -a chosen < <(printf "%s\n" "$selected" && printf '\0') +else + # Non-interactive: choose all + chosen=("${filtered[@]}") +fi + +# Worktree destination is current working directory +worktree_root=$(pwd) + +echo "Repository root: $repo_root" +echo "Worktree root : $worktree_root" + +# Create symlinks +created=() +skipped=() +errors=() + +for rel in "${chosen[@]}"; do + # Trim trailing newlines/spaces + rel=${rel%%$'\n'} + src="$repo_root/$rel" + dst="$worktree_root/$rel" + + if [ ! -e "$src" ]; then + errors+=("$rel (source missing)") + continue + fi + + if [ -L "$dst" ]; then + # Already a symlink + echo "Skipping $rel (already symlink)" + skipped+=("$rel") + continue + fi + if [ -e "$dst" ]; then + echo "Skipping $rel (destination exists)" + skipped+=("$rel") + continue + fi + + mkdir -p "$(dirname "$dst")" + if [ "$DRY_RUN" -eq 1 ]; then + echo "DRY RUN: ln -s '$src' '$dst'" + else + if ln -s "$src" "$dst"; then + echo "Linked: $rel" + created+=("$rel") + else + echo "Failed to link: $rel" >&2 + errors+=("$rel (link failed)") + fi + fi +done + +# Summary +echo +echo "Summary:" +echo " Linked: ${#created[@]}" +[ ${#created[@]} -gt 0 ] && printf ' %s\n' "${created[@]}" +echo " Skipped: ${#skipped[@]}" +[ ${#skipped[@]} -gt 0 ] && printf ' %s\n' "${skipped[@]}" +echo " Errors: ${#errors[@]}" +[ ${#errors[@]} -gt 0 ] && printf ' %s\n' "${errors[@]}" + +exit 0 From 186e8db249216bcea84d776a8b050fda98f53efa Mon Sep 17 00:00:00 2001 From: "RingOfStorms (Joshua Bell)" Date: Thu, 11 Sep 2025 09:28:12 -0500 Subject: [PATCH 3/4] nix: expose branch, branchd, and link_ignored via writeShellScriptBin; refactor branch logic --- common/general/shell/common.nix | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/common/general/shell/common.nix b/common/general/shell/common.nix index b78ebe4..baa7977 100644 --- a/common/general/shell/common.nix +++ b/common/general/shell/common.nix @@ -25,6 +25,17 @@ with lib; hdparm speedtest-cli lf + + # Build script bins from repo scripts + (pkgs.writeShellScriptBin "branch" ''#!/usr/bin/env bash +. ${./branch.sh} +branch "$@" +'') + (pkgs.writeShellScriptBin "branchd" ''#!/usr/bin/env bash +. ${./branch.sh} +branchd "$@" +'') + (pkgs.writeShellScriptBin "link_ignored" (builtins.readFile ./link_ignored.sh)) ]; environment.shellAliases = { @@ -53,6 +64,7 @@ with lib; gcm = "git commit -m"; stashes = "git stash list"; + # ripgrep rg = "rg --no-ignore"; rgf = "rg --files --glob '!/nix/store/**' 2>/dev/null | rg"; From 951dd38e9d418856a50b86172744800819cf7807 Mon Sep 17 00:00:00 2001 From: "RingOfStorms (Joshua Bell)" Date: Thu, 11 Sep 2025 13:49:58 -0500 Subject: [PATCH 4/4] new git working strategy, using worktrees now for easier parallel development --- common/general/shell/branch.func.sh | 105 +++++++++++++ common/general/shell/branch.sh | 113 -------------- common/general/shell/branchd.func.sh | 55 +++++++ common/general/shell/common.nix | 19 +-- common/general/shell/common.sh | 85 +---------- common/general/shell/link_ignored.func.sh | 152 +++++++++++++++++++ common/general/shell/link_ignored.sh | 175 ---------------------- hosts/h003/mods/networking.nix | 1 + 8 files changed, 320 insertions(+), 385 deletions(-) create mode 100644 common/general/shell/branch.func.sh delete mode 100644 common/general/shell/branch.sh create mode 100644 common/general/shell/branchd.func.sh create mode 100644 common/general/shell/link_ignored.func.sh delete mode 100755 common/general/shell/link_ignored.sh diff --git a/common/general/shell/branch.func.sh b/common/general/shell/branch.func.sh new file mode 100644 index 0000000..550a950 --- /dev/null +++ b/common/general/shell/branch.func.sh @@ -0,0 +1,105 @@ +branch() { + local branch_name=${1:-} + if [ -z "$branch_name" ]; then + echo "Usage: branch " >&2 + return 2 + fi + + # branch — create or open a worktree for + # This function will change directory into the selected worktree so the + # caller's shell is moved into it. + + local xdg=${XDG_DATA_HOME:-$HOME/.local/share} + + local common_dir + if ! common_dir=$(git rev-parse --git-common-dir 2>/dev/null); then + echo "Not inside a git repository." >&2 + return 1 + fi + if [ "${common_dir#/}" = "$common_dir" ]; then + common_dir="$(pwd)/$common_dir" + fi + local repo_dir="${common_dir%%/.git*}" + if [ -z "$repo_dir" ]; then + echo "Unable to determine repository root." >&2 + return 1 + fi + + local repo_base repo_hash default_branch + repo_base=$(basename "$repo_dir") + repo_hash=$(printf "%s" "$repo_dir" | sha1sum | awk '{print $1}') + + default_branch=$(getdefault) + + # Special-case: jump to the main working tree on the default branch + if [ "$branch_name" = "default" ] || [ "$branch_name" = "master" ] || [ "$branch_name" = "$default_branch" ]; then + if [ "$repo_dir" = "$PWD" ]; then + echo "Already in the main working tree on branch '$default_branch'." + return 0 + fi + echo "Switching to main working tree on branch '$default_branch'." + cd "$repo_dir" || return 1 + return 0 + fi + + # If a worktree for this branch is already registered elsewhere, open a shell there + local existing + existing=$(git -C "$repo_dir" worktree list --porcelain 2>/dev/null | awk -v b="$branch_name" 'BEGIN{RS="";FS="\n"} $0 ~ "refs/heads/"b{for(i=1;i<=NF;i++) if ($i ~ /^worktree /){ sub(/^worktree /,"",$i); print $i }}') + if [ -n "$existing" ]; then + echo "Opening existing worktree for branch '$branch_name' at '$existing'." + cd "$existing" || return 1 + return 0 + fi + + # Ensure we have up-to-date remote info + git -C "$repo_dir" fetch --all --prune || true + + local wt_root wt_path + wt_root="$xdg/git_worktrees/${repo_base}_${repo_hash}" + wt_path="$wt_root/$branch_name" + + # If worktree already exists at our expected path, open a shell there + if [ -d "$wt_path" ]; then + echo "Opening existing worktree at '$wt_path'." + cd "$wt_path" || return 1 + return 0 + fi + + local branch_exists branch_from + branch_exists=$(git -C "$repo_dir" ls-remote --heads origin "$branch_name" | wc -l) + branch_from="$default_branch" + if [ "$branch_exists" -eq 0 ]; then + echo "Branch '$branch_name' does not exist on remote; creating from '$branch_from'." + else + branch_from="origin/$branch_name" + echo "Branch '$branch_name' exists on remote; creating worktree tracking it." + fi + + echo "Creating new worktree for branch '$branch_name' at '$wt_path'." + + # Try to add or update worktree from the resolved ref. Use a fallback path if needed. + if git -C "$repo_dir" worktree add -b "$branch_name" "$wt_path" "$branch_from" 2>/dev/null; then + cd "$wt_path" || return 1 + return 0 + fi + + # Fallback: try to resolve a concrete SHA and create the branch ref locally, then add worktree + local start_sha + if start_sha=$(git -C "$repo_dir" rev-parse --verify "$branch_from" 2>/dev/null); then + if git -C "$repo_dir" branch "$branch_name" "$start_sha" 2>/dev/null; then + if git -C "$repo_dir" worktree add "$wt_path" "$branch_name" 2>/dev/null; then + cd "$wt_path" || return 1 + return 0 + else + git -C "$repo_dir" branch -D "$branch_name" 2>/dev/null || true + rmdir "$wt_path" 2>/dev/null || true + echo "Failed to add worktree after creating branch ref." >&2 + return 1 + fi + fi + fi + + echo "Failed to add worktree for branch '$branch_name'." >&2 + rmdir "$wt_path" 2>/dev/null || true + return 1 +} diff --git a/common/general/shell/branch.sh b/common/general/shell/branch.sh deleted file mode 100644 index a8f6807..0000000 --- a/common/general/shell/branch.sh +++ /dev/null @@ -1,113 +0,0 @@ -# Branch and branchd helpers (worktree-based) - -# branch — create or jump to a worktree for -branch() { - # Use XDG_DATA_HOME or default to ~/.local/share - local xdg=${XDG_DATA_HOME:-$HOME/.local/share} - # Determine the git common dir (points into the main repo's .git) - local common_dir - common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || { - echo "Not inside a git repository." >&2 - return 1 - } - # Make common_dir absolute if it's relative - if [ "${common_dir#/}" = "$common_dir" ]; then - common_dir="$(pwd)/$common_dir" - fi - # repo_dir is the path before '/.git' in the common_dir (handles worktrees) - local repo_dir - repo_dir="${common_dir%%/.git*}" - if [ -z "$repo_dir" ]; then - echo "Unable to determine repository root." >&2 - return 1 - fi - - local repo_base - repo_base=$(basename "$repo_dir") - local repo_hash - repo_hash=$(printf "%s" "$repo_dir" | sha1sum | awk '{print $1}') - - local branch_name - branch_name=$1 - if [ -z "$branch_name" ]; then - echo "Usage: branch " >&2 - return 1 - fi - - # If user asked for default or master, cd back to repo root on default branch - local default_branch - default_branch=$(getdefault 2>/dev/null || echo "") - if [ "$branch_name" = "default" ] || [ "$branch_name" = "master" ] || [ "$branch_name" = "$default_branch" ]; then - cd "$repo_dir" || return 0 - git fetch - git checkout "$default_branch" - pull - return 0 - fi - - # Ensure we have up-to-date remote info - git fetch --all --prune - - # If branch exists remotely and not locally, create local branch tracking remote - if git ls-remote --exit-code --heads origin "$branch_name" >/dev/null 2>&1; then - if ! git show-ref --verify --quiet "refs/heads/$branch_name"; then - git branch --track "$branch_name" "origin/$branch_name" 2>/dev/null || git branch "$branch_name" "origin/$branch_name" - fi - fi - - # Worktree path - local wt_root - wt_root="$xdg/git_worktrees/${repo_base}_${repo_hash}" - local wt_path - wt_path="$wt_root/$branch_name" - - mkdir -p "$wt_root" - - # If worktree already exists at our expected path, cd to it - if [ -d "$wt_path/.git" ]; then - cd "$wt_path" || return 0 - return 0 - fi - - # If a worktree for this branch is already registered elsewhere, find it and cd - local existing - existing=$(git worktree list --porcelain 2>/dev/null | awk -v b="$branch_name" 'BEGIN{RS=""} $0 ~ "refs/heads/"b{for(i=1;i<=NF;i++) if ($i ~ /^worktree/) print $2 }') - if [ -n "$existing" ]; then - cd "$existing" || return 0 - return 0 - fi - - # Create the worktree - mkdir -p "$wt_path" - git worktree add -B "$branch_name" "$wt_path" "origin/$branch_name" 2>/dev/null || git worktree add "$wt_path" "$branch_name" - cd "$wt_path" || return 0 -} - -# branchd — remove current branch worktree -branchd() { - local current - current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || { - echo "Not inside a git repository." >&2 - return 1 - } - local default_branch - default_branch=$(getdefault 2>/dev/null || echo "") - - if [ "$current" = "$default_branch" ] || [ "$current" = "default" ]; then - echo "Already on default branch ($default_branch). Won't remove." >&2 - return 1 - fi - - # Find the worktree path for the current branch - local wt_path - wt_path=$(git worktree list --porcelain 2>/dev/null | awk -v b="$current" 'BEGIN{RS="";FS="\n"} $0 ~ "refs/heads/"b{for(i=1;i<=NF;i++) if ($i ~ /^worktree /) { sub(/^worktree /,"",$i); print $i }}') - if [ -z "$wt_path" ]; then - echo "Worktree for branch '$current' not found." >&2 - return 1 - fi - - # Switch to default branch (uses branch() helper) and then remove worktree - branch default || { echo "Failed to switch to default branch" >&2; return 1; } - - git worktree remove "$wt_path" -} diff --git a/common/general/shell/branchd.func.sh b/common/general/shell/branchd.func.sh new file mode 100644 index 0000000..7fc296c --- /dev/null +++ b/common/general/shell/branchd.func.sh @@ -0,0 +1,55 @@ +branchdel() { + # branchd — remove current branch worktree (function form) + local wt_path + wt_path=$(pwd) + local common_dir repo_dir + if ! common_dir=$(git rev-parse --git-common-dir 2>/dev/null); then + echo "Not inside a git repository." >&2 + return 1 + fi + if [ "${common_dir#/}" = "$common_dir" ]; then + common_dir="$(pwd)/$common_dir" + fi + repo_dir="${common_dir%%/.git*}" + if [ -z "$repo_dir" ]; then + echo "Unable to determine repository root." >&2 + return 1 + fi + + if [ "$repo_dir" = "$wt_path" ]; then + echo "Inside the root directory of repo, will not delete." >&2 + return 1 + fi + + local current default_branch + current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || { echo "Not inside a git repository." >&2; return 1; } + + default_branch=$(getdefault) + + if [ "$current" = "$default_branch" ] || [ "$current" = "default" ]; then + echo "Already on default branch ($default_branch). Won't remove." + return 0 + fi + + echo "Switching to default branch '$default_branch'..." + # Use branch function if available, otherwise checkout directly in repo + if declare -f branch >/dev/null 2>&1; then + branch default || { echo "Failed to switch to default branch" >&2; return 1; } + else + git -C "$repo_dir" checkout "$default_branch" || { echo "Failed to checkout default branch" >&2; return 1; } + fi + + echo "Removing worktree at: $wt_path" + if git -C "$repo_dir" worktree remove "$wt_path" 2>/dev/null; then + echo "Removed worktree: $wt_path" + return 0 + fi + # try with --force as a fallback + if git -C "$repo_dir" worktree remove --force "$wt_path" 2>/dev/null; then + echo "Removed worktree (forced): $wt_path" + return 0 + fi + + echo "Failed to remove worktree: $wt_path" >&2 + return 1 +} diff --git a/common/general/shell/common.nix b/common/general/shell/common.nix index baa7977..16f2026 100644 --- a/common/general/shell/common.nix +++ b/common/general/shell/common.nix @@ -25,17 +25,6 @@ with lib; hdparm speedtest-cli lf - - # Build script bins from repo scripts - (pkgs.writeShellScriptBin "branch" ''#!/usr/bin/env bash -. ${./branch.sh} -branch "$@" -'') - (pkgs.writeShellScriptBin "branchd" ''#!/usr/bin/env bash -. ${./branch.sh} -branchd "$@" -'') - (pkgs.writeShellScriptBin "link_ignored" (builtins.readFile ./link_ignored.sh)) ]; environment.shellAliases = { @@ -64,12 +53,16 @@ branchd "$@" gcm = "git commit -m"; stashes = "git stash list"; - # ripgrep rg = "rg --no-ignore"; rgf = "rg --files --glob '!/nix/store/**' 2>/dev/null | rg"; }; - environment.shellInit = builtins.readFile ./common.sh; + environment.shellInit = lib.concatStringsSep "\n\n" [ + (builtins.readFile ./common.sh) + (builtins.readFile ./branch.func.sh) + (builtins.readFile ./branchd.func.sh) + (builtins.readFile ./link_ignored.func.sh) + ]; }; } diff --git a/common/general/shell/common.sh b/common/general/shell/common.sh index 00b5d3d..8f88702 100644 --- a/common/general/shell/common.sh +++ b/common/general/shell/common.sh @@ -71,7 +71,7 @@ getdefault () { } master () { - git stash + branch $(getdefault) git checkout $(getdefault) pull } @@ -129,89 +129,6 @@ prunel () { done; } -branch() { - local branch_name - branch_name=$1 - if [ -z "$branch_name" ]; then - echo "Usage: branch " >&2 - return 1 - fi - - # Use XDG_DATA_HOME or default to ~/.local/share - local xdg=${XDG_DATA_HOME:-$HOME/.local/share} - local repo_dir - # Determine the git common dir (points into the main repo's .git) - local common_dir - common_dir=$(git rev-parse --git-common-dir 2>/dev/null) || { - echo "Not inside a git repository." >&2 - return 1 - } - # Make common_dir absolute if it's relative - if [ "${common_dir#/}" = "$common_dir" ]; then - common_dir="$(pwd)/$common_dir" - fi - # repo_dir is the path before '/.git' in the common_dir (handles worktrees) - repo_dir="${common_dir%%/.git*}" - if [ -z "$repo_dir" ]; then - echo "Unable to determine repository root." >&2 - return 1 - fi - - local repo_base - repo_base=$(basename "$repo_dir") - local repo_hash - repo_hash=$(printf "%s" "$repo_dir" | sha1sum | awk '{print $1}') - - # If user asked for default or master, cd back to repo root on default branch - local default_branch - default_branch=$(getdefault 2>/dev/null || echo "") - if [ "$branch_name" = "default" ] || [ "$branch_name" = "master" ] || [ "$branch_name" = "$default_branch" ]; then - cd "$repo_dir" || return 0 - # git fetch - # git checkout "$default_branch" - # pull - return 0 - fi - - # Ensure we have up-to-date remote info - git fetch --all --prune - - # If branch exists remotely and not locally, create local branch tracking remote - if git ls-remote --exit-code --heads origin "$branch_name" >/dev/null 2>&1; then - if ! git show-ref --verify --quiet "refs/heads/$branch_name"; then - git branch --track "$branch_name" "origin/$branch_name" 2>/dev/null || git branch "$branch_name" "origin/$branch_name" - fi - fi - - # Worktree path - local wt_root - wt_root="$xdg/git_worktrees/${repo_base}_${repo_hash}" - local wt_path - wt_path="$wt_root/$branch_name" - - mkdir -p "$wt_root" - - # If worktree already exists, cd to it - if [ -d "$wt_path/.git" ] || [ -d "$wt_path" -a -d "$wt_path/.git" ]; then - cd "$wt_path" || return 0 - return 0 - fi - - # If a worktree for this branch is already registered elsewhere, find it and cd - local existing - existing=$(git worktree list --porcelain 2>/dev/null | awk -v b="$branch_name" 'BEGIN{RS=""} $0 ~ "refs/heads/"b{for(i=1;i<=NF;i++) if ($i ~ /^worktree/) print $2 }') - if [ -n "$existing" ]; then - cd "$existing" || return 0 - return 0 - fi - - # Create the worktree - mkdir -p "$wt_path" - git worktree add -B "$branch_name" "$wt_path" "origin/$branch_name" 2>/dev/null || git worktree add "$wt_path" "$branch_name" - cd "$wt_path" || return 0 -} - - from_master () { git checkout $(getdefault) $@ } diff --git a/common/general/shell/link_ignored.func.sh b/common/general/shell/link_ignored.func.sh new file mode 100644 index 0000000..fdc6848 --- /dev/null +++ b/common/general/shell/link_ignored.func.sh @@ -0,0 +1,152 @@ +link_ignored() { + local DRY_RUN=0 + local USE_FZF=1 + local -a PATTERNS=() + + while [ $# -gt 0 ]; do + case "$1" in + --dry-run) DRY_RUN=1; shift ;; + --no-fzf) USE_FZF=0; shift ;; + -h|--help) link_ignored_usage; return 0 ;; + --) shift; break ;; + *) PATTERNS+=("$1"); shift ;; + esac + done + + link_ignored_usage() { + cat </dev/null); then + echo "Error: not in a git repository." >&2 + return 2 + fi + if [ "${common_dir#/}" = "$common_dir" ]; then + common_dir="$(pwd)/$common_dir" + fi + repo_root="${common_dir%%/.git*}" + if [ -z "$repo_root" ]; then + echo "Error: unable to determine repository root." >&2 + return 2 + fi + + local -a candidates + IFS=$'\0' read -r -d '' -a candidates < <(git -C "$repo_root" ls-files --others --ignored --exclude-standard -z || true) + + if [ ${#candidates[@]} -eq 0 ]; then + echo "No untracked/ignored files found in $repo_root" + return 0 + fi + + declare -A _seen + local -a tops=() + for c in "${candidates[@]}"; do + c="${c%/}" + local top="${c%%/*}" + [ -z "$top" ] && continue + if [ -z "${_seen[$top]:-}" ]; then + _seen[$top]=1 + tops+=("$top") + fi + done + + if [ ${#tops[@]} -eq 0 ]; then + echo "No top-level ignored/untracked entries found in $repo_root" + return 0 + fi + + local -a filtered + if [ ${#PATTERNS[@]} -gt 0 ]; then + for t in "${tops[@]}"; do + for p in "${PATTERNS[@]}"; do + if [[ "$t" == *"$p"* ]]; then + filtered+=("$t") + break + fi + done + done + else + filtered=("${tops[@]}") + fi + + if [ ${#filtered[@]} -eq 0 ]; then + echo "No candidates match the provided patterns." >&2 + return 0 + fi + + local -a chosen + if command -v fzf >/dev/null 2>&1 && [ "$USE_FZF" -eq 1 ]; then + local selected + selected=$(printf "%s\n" "${filtered[@]}" | fzf --multi --height=40% --border --prompt="Select files to link: " --preview "if [ -f '$repo_root'/{} ]; then bat --color always --paging=never --style=plain '$repo_root'/{}; else ls -la '$repo_root'/{}; fi") + if [ -z "$selected" ]; then + echo "No files selected." && return 0 + fi + IFS=$'\n' read -r -d '' -a chosen < <(printf "%s\n" "$selected" && printf '\0') + else + chosen=("${filtered[@]}") + fi + + local worktree_root + worktree_root=$(pwd) + + echo "Repository root: $repo_root" + echo "Worktree root : $worktree_root" + + local -a created=() + local -a skipped=() + local -a errors=() + + for rel in "${chosen[@]}"; do + rel=${rel%%$'\n'} + local src="${repo_root}/${rel}" + local dst="${worktree_root}/${rel}" + + if [ ! -e "$src" ]; then + errors+=("$rel (source missing)") + continue + fi + + if [ -L "$dst" ]; then + echo "Skipping $rel (already symlink)" + skipped+=("$rel") + continue + fi + if [ -e "$dst" ]; then + echo "Skipping $rel (destination exists)" + skipped+=("$rel") + continue + fi + + mkdir -p "$(dirname "$dst")" + if [ "$DRY_RUN" -eq 1 ]; then + echo "DRY RUN: ln -s '$src' '$dst'" + else + if ln -s "$src" "$dst"; then + echo "Linked: $rel" + created+=("$rel") + else + echo "Failed to link: $rel" >&2 + errors+=("$rel (link failed)") + fi + fi + done + + echo + echo "Summary:" + echo " Linked: ${#created[@]}" + [ ${#created[@]} -gt 0 ] && printf ' %s\n' "${created[@]}" + echo " Skipped: ${#skipped[@]}" + [ ${#skipped[@]} -gt 0 ] && printf ' %s\n' "${skipped[@]}" + echo " Errors: ${#errors[@]}" + [ ${#errors[@]} -gt 0 ] && printf ' %s\n' "${errors[@]}" + + return 0 +} diff --git a/common/general/shell/link_ignored.sh b/common/general/shell/link_ignored.sh deleted file mode 100755 index 95c0a71..0000000 --- a/common/general/shell/link_ignored.sh +++ /dev/null @@ -1,175 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -usage() { - cat </dev/null); then - echo "Error: not in a git repository." >&2 - exit 2 -fi -# Make absolute if relative -if [ "${common_dir#/}" = "$common_dir" ]; then - common_dir="$(pwd)/$common_dir" -fi -repo_root="${common_dir%%/.git*}" -if [ -z "$repo_root" ]; then - echo "Error: unable to determine repository root." >&2 - exit 2 -fi - -# Get list of untracked/ignored files from the repo root (relative paths) -mapfile -d $'\0' -t candidates < <(git -C "$repo_root" ls-files --others --ignored --exclude-standard -z || true) - -if [ ${#candidates[@]} -eq 0 ]; then - echo "No untracked/ignored files found in $repo_root" - exit 0 -fi - -# Collapse to top-level (first path component) and make unique. -# This prevents listing every file under node_modules/ or build/. -declare -A _seen -tops=() -for c in "${candidates[@]}"; do - # remove trailing slash if present - c="${c%/}" - top="${c%%/*}" - [ -z "$top" ] && continue - if [ -z "${_seen[$top]:-}" ]; then - _seen[$top]=1 - tops+=("$top") - fi -done - -if [ ${#tops[@]} -eq 0 ]; then - echo "No top-level ignored/untracked entries found in $repo_root" - exit 0 -fi - -# Filter top-level entries by provided patterns (if any) -if [ ${#PATTERNS[@]} -gt 0 ]; then - filtered=() - for t in "${tops[@]}"; do - for p in "${PATTERNS[@]}"; do - if [[ "$t" == *"$p"* ]]; then - filtered+=("$t") - break - fi - done - done -else - filtered=("${tops[@]}") -fi - -if [ ${#filtered[@]} -eq 0 ]; then - echo "No candidates match the provided patterns." >&2 - exit 0 -fi - -# Present selection -if command -v fzf >/dev/null 2>&1 && [ "$USE_FZF" -eq 1 ]; then - # Show preview of the source file (if text) and allow multi-select - selected=$(printf "%s\n" "${filtered[@]}" | fzf --multi --height=40% --border --prompt="Select files to link: " --preview "if [ -f '$repo_root'/{} ]; then bat --color always --paging=never --style=plain '$repo_root'/{}; else ls -la '$repo_root'/{}; fi") - if [ -z "$selected" ]; then - echo "No files selected." && exit 0 - fi - # Convert to array - IFS=$'\n' read -r -d '' -a chosen < <(printf "%s\n" "$selected" && printf '\0') -else - # Non-interactive: choose all - chosen=("${filtered[@]}") -fi - -# Worktree destination is current working directory -worktree_root=$(pwd) - -echo "Repository root: $repo_root" -echo "Worktree root : $worktree_root" - -# Create symlinks -created=() -skipped=() -errors=() - -for rel in "${chosen[@]}"; do - # Trim trailing newlines/spaces - rel=${rel%%$'\n'} - src="$repo_root/$rel" - dst="$worktree_root/$rel" - - if [ ! -e "$src" ]; then - errors+=("$rel (source missing)") - continue - fi - - if [ -L "$dst" ]; then - # Already a symlink - echo "Skipping $rel (already symlink)" - skipped+=("$rel") - continue - fi - if [ -e "$dst" ]; then - echo "Skipping $rel (destination exists)" - skipped+=("$rel") - continue - fi - - mkdir -p "$(dirname "$dst")" - if [ "$DRY_RUN" -eq 1 ]; then - echo "DRY RUN: ln -s '$src' '$dst'" - else - if ln -s "$src" "$dst"; then - echo "Linked: $rel" - created+=("$rel") - else - echo "Failed to link: $rel" >&2 - errors+=("$rel (link failed)") - fi - fi -done - -# Summary -echo -echo "Summary:" -echo " Linked: ${#created[@]}" -[ ${#created[@]} -gt 0 ] && printf ' %s\n' "${created[@]}" -echo " Skipped: ${#skipped[@]}" -[ ${#skipped[@]} -gt 0 ] && printf ' %s\n' "${skipped[@]}" -echo " Errors: ${#errors[@]}" -[ ${#errors[@]} -gt 0 ] && printf ' %s\n' "${errors[@]}" - -exit 0 diff --git a/hosts/h003/mods/networking.nix b/hosts/h003/mods/networking.nix index 9521d22..e07611e 100644 --- a/hosts/h003/mods/networking.nix +++ b/hosts/h003/mods/networking.nix @@ -211,6 +211,7 @@ "2c:cf:67:6a:45:47,HOMEASSISTANT,10.12.14.22" "2a:d0:ec:fa:b9:7e,PIXEL-6,10.12.14.31" "a8:29:48:94:23:dd,TL-SG1428PE,10.12.16.2" + "00:23:a4:0b:3b:be,TMREM00004335,10.12.14.181" ]; enable-ra = lib.mkIf config.networking.enableIPv6 true;