From 781404749e8346beb2136768341e74ea15fd4db7 Mon Sep 17 00:00:00 2001 From: reakjra Date: Tue, 18 Nov 2025 14:32:05 +0100 Subject: [PATCH] exp-merge subcommand script --- sdata/subcmd-exp-merge/0.run.sh | 325 ++++++++++++++++++++++++++++++ sdata/subcmd-exp-merge/options.sh | 58 ++++++ setup | 3 +- 3 files changed, 385 insertions(+), 1 deletion(-) create mode 100644 sdata/subcmd-exp-merge/0.run.sh create mode 100644 sdata/subcmd-exp-merge/options.sh diff --git a/sdata/subcmd-exp-merge/0.run.sh b/sdata/subcmd-exp-merge/0.run.sh new file mode 100644 index 000000000..4afff5ad4 --- /dev/null +++ b/sdata/subcmd-exp-merge/0.run.sh @@ -0,0 +1,325 @@ +# shellcheck shell=bash + +set -euo pipefail + +MERGE_BRANCH="exp-merge-branch" +BACKUP_DIR="${REPO_ROOT}/.exp-merge-backups" + +cleanup_on_exit() { + local exit_code=$? + if [[ $exit_code -ne 0 ]]; then + echo + log_warning "Script interrupted or failed" + if git status 2>/dev/null | grep -q "rebase in progress"; then + echo + echo -e "${STY_YELLOW}Rebase is still in progress${STY_RST}" + echo "Continue: git rebase --continue" + echo "Abort: git rebase --abort" + fi + fi +} + +trap cleanup_on_exit EXIT INT TERM + +check_preconditions() { + log_header "Checking Preconditions" + + cd "$REPO_ROOT" || log_die "Failed to change to repository directory" + + if ! git rev-parse --is-inside-work-tree &>/dev/null; then + log_die "Not in a git repository" + fi + + if ! git diff --quiet || ! git diff --cached --quiet; then + log_error "You have uncommitted changes in the repository:" + git status --short + log_die "Please commit or stash your changes before running exp-merge" + fi + + if ! git remote get-url upstream &>/dev/null; then + log_die "No remote 'upstream' configured" + fi + + log_success "Precondition checks passed" +} + +fetch_upstream() { + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would fetch from upstream" + return + fi + + if [[ "$SKIP_FETCH" == false ]]; then + log_info "Fetching from upstream..." + git fetch upstream || log_die "Failed to fetch from upstream" + log_success "Fetched from upstream" + else + log_info "Skipping fetch (--skip-fetch flag set)" + fi +} + +update_main_with_upstream() { + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would update main from upstream" + return + fi + + log_info "Updating main with upstream..." + git checkout main + git merge --ff-only upstream/main || log_die "Main has diverged from upstream, cannot fast-forward" + log_success "Main updated" +} + +switch_to_merge_branch() { + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would switch to merge branch" + return + fi + + # check if branch exists + if git show-ref --verify --quiet "refs/heads/${MERGE_BRANCH}"; then + log_info "Switching to existing merge branch..." + git checkout "${MERGE_BRANCH}" + else + log_info "Creating new merge branch from main..." + git checkout -b "${MERGE_BRANCH}" + fi + log_success "On branch ${MERGE_BRANCH}" +} + +copy_and_commit_user_config() { + local user_quickshell="${HOME}/.config/quickshell" + local repo_quickshell="${REPO_ROOT}/dots/.config/quickshell" + + if [[ ! -d "${user_quickshell}" ]]; then + log_warning "Quickshell config not found at: ${user_quickshell}" + log_info "Skipping" + return 1 + fi + + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would copy and commit user config" + return + fi + + # chekc for rebase in progress + if git status | grep -q "rebase in progress"; then + log_error "Rebase already in progress, resolve it first" + return 1 + fi + + log_info "Copying user config..." + rm -rf "${repo_quickshell}" + cp -r "${user_quickshell}" "${repo_quickshell}" + find "${repo_quickshell}" \( -name '.git' -o -name '.gitmodules' \) -exec rm -rf {} + 2>/dev/null || true + + git add . + if git diff --cached --quiet; then + log_info "No changes to commit" + else + git commit -m "user changes" + log_success "Committed user changes" + fi +} + +rebase_onto_main() { + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would rebase onto main" + return + fi + + log_info "Rebasing onto main..." + if git rebase main; then + log_success "Rebase completed" + else + log_error "Rebase encountered conflicts" + echo + echo -e "${STY_YELLOW}Conflicted files:${STY_RST}" + git diff --name-only --diff-filter=U + echo + echo -e "${STY_CYAN}To resolve:${STY_RST}" + echo " 1. Edit conflicted files" + echo " 2. git add " + echo " 3. git rebase --continue" + echo " 4. Run this script again" + echo + echo -e "${STY_CYAN}To abort:${STY_RST}" + echo " git rebase --abort" + echo + return 1 + fi +} + +apply_quickshell_config() { + log_header "Apply Quickshell Config" + + local user_quickshell="${HOME}/.config/quickshell" + local repo_quickshell="${REPO_ROOT}/dots/.config/quickshell" + local timestamp + timestamp=$(date +%Y%m%d-%H%M%S) + + echo + echo -e "${STY_CYAN}Your quickshell config has been merged with upstream.${STY_RST}" + echo "What to do with merged config:" + echo + echo "1) Replace current with merged version" + echo "2) Backup current, then replace" + echo "3) Save merged as quickshell.new" + echo "4) Skip" + echo + + local choice + read -p "Choice (1-4): " choice + + case $choice in + 1) + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would replace config" + else + rm -rf "${user_quickshell}" + cp -r "${repo_quickshell}" "${user_quickshell}" + log_success "Config replaced" + fi + ;; + 2) + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would backup and replace" + else + mkdir -p "${BACKUP_DIR}" + local backup_name="quickshell.${timestamp}.bak" + cp -r "${user_quickshell}" "${BACKUP_DIR}/${backup_name}" + log_success "Backup: ${backup_name}" + rm -rf "${user_quickshell}" + cp -r "${repo_quickshell}" "${user_quickshell}" + log_success "Config replaced" + fi + ;; + 3) + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would save as quickshell.new" + else + local new_config="${HOME}/.config/quickshell.new" + rm -rf "${new_config}" + cp -r "${repo_quickshell}" "${new_config}" + log_success "Saved as quickshell.new" + log_info "Current config unchanged" + fi + ;; + 4) + log_info "Skipped" + ;; + *) + log_warning "Invalid choice" + ;; + esac +} + +update_hypr_config() { + log_header "Update Hyprland Config" + + local user_hypr="${HOME}/.config/hypr" + local repo_hypr="${REPO_ROOT}/dots/.config/hypr" + local timestamp + timestamp=$(date +%Y%m%d-%H%M%S) + + if [[ ! -d "${user_hypr}" ]] || [[ ! -d "${repo_hypr}" ]]; then + log_info "Hypr config not found, skipping" + return + fi + + echo + echo -e "${STY_CYAN}Update hyprland config?${STY_RST}" + echo -e "${STY_YELLOW}Note: /custom/ directory will be preserved${STY_RST}" + echo + echo "1) Update now" + echo "2) Backup, then update" + echo "3) Skip" + echo + + local choice + read -p "Choice (1-3): " choice + + case $choice in + 1) + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would update hypr" + else + local temp_custom="/tmp/hypr-custom-${timestamp}" + [[ -d "${user_hypr}/custom" ]] && cp -r "${user_hypr}/custom" "${temp_custom}" + rm -rf "${user_hypr}" + cp -r "${repo_hypr}" "${user_hypr}" + if [[ -d "${temp_custom}" ]]; then + rm -rf "${user_hypr}/custom" + cp -r "${temp_custom}" "${user_hypr}/custom" + rm -rf "${temp_custom}" + fi + log_success "Hypr updated" + fi + ;; + 2) + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would backup and update" + else + mkdir -p "${BACKUP_DIR}" + local backup_name="hypr.${timestamp}.bak" + cp -r "${user_hypr}" "${BACKUP_DIR}/${backup_name}" + log_success "Backup: ${backup_name}" + + local temp_custom="/tmp/hypr-custom-${timestamp}" + [[ -d "${user_hypr}/custom" ]] && cp -r "${user_hypr}/custom" "${temp_custom}" + rm -rf "${user_hypr}" + cp -r "${repo_hypr}" "${user_hypr}" + if [[ -d "${temp_custom}" ]]; then + rm -rf "${user_hypr}/custom" + cp -r "${temp_custom}" "${user_hypr}/custom" + rm -rf "${temp_custom}" + fi + log_success "Hypr updated" + fi + ;; + 3) + log_info "Skipped" + ;; + *) + log_warning "Invalid choice" + ;; + esac +} + +log_header "Experimental Config Merge" + +check_preconditions + +fetch_upstream + +update_main_with_upstream + +log_header "Merging Quickshell Config" + +switch_to_merge_branch + +if copy_and_commit_user_config; then + if rebase_onto_main; then + apply_quickshell_config + fi +fi + +update_hypr_config + +# back to main +if [[ "$DRY_RUN" != true ]]; then + log_info "Switching back to main..." + git checkout main +fi + +log_header "Merge Complete" + +if [[ "$DRY_RUN" == true ]]; then + log_warning "DRY-RUN: No changes made" +else + log_success "Done" +fi + +[[ -d "${BACKUP_DIR}" ]] && log_info "Backups in: ${BACKUP_DIR}/" + +echo diff --git a/sdata/subcmd-exp-merge/options.sh b/sdata/subcmd-exp-merge/options.sh new file mode 100644 index 000000000..e1185da9b --- /dev/null +++ b/sdata/subcmd-exp-merge/options.sh @@ -0,0 +1,58 @@ +# Handle args for subcmd: exp-merge +# shellcheck shell=bash + +showhelp(){ +echo -e "Syntax: $0 exp-merge [OPTIONS]... + +Experimental config merging using git rebase. +Merges upstream changes with your quickshell config. + +Options: + -n, --dry-run Show what would be done + -h, --help Show this help + --skip-fetch Skip fetching from remote + +How it works: + 1. Fetch from upstream + 2. Update main branch + 3. Switch to exp-merge-branch (persistent) + 4. Copy your ~/.config/quickshell and commit + 5. Rebase onto main (3-way merge with history) + 6. Prompt to apply merged config + 7. Optionally update hypr config (preserves cstom folder) + 8. Switch back to main +" +} + +para=$(getopt \ + -o hn \ + -l help,dry-run,skip-fetch \ + -n "$0" -- "$@") +[ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1 + +eval set -- "$para" +while true ; do + case "$1" in + -h|--help) showhelp;exit;; + --) break ;; + *) shift ;; + esac +done + +DRY_RUN=false +SKIP_FETCH=false + +eval set -- "$para" +while true ; do + case "$1" in + -n|--dry-run) DRY_RUN=true;shift + log_info "Dry-run mode enabled - no changes will be made" + ;; + --skip-fetch) SKIP_FETCH=true;shift + log_info "Skipping fetch from remote" + ;; + + --) break ;; + *) echo -e "$0: Wrong parameters.";exit 1;; + esac +done diff --git a/setup b/setup index cc5fca8ab..ee2fba78f 100755 --- a/setup +++ b/setup @@ -33,6 +33,7 @@ Subcommands: exp-uninstall (Experimental) Uninstall illogical-impulse. exp-update (Experimental) Update illogical-impulse without fully reinstall. exp-update-old (Experimental) exp-update but use behaves like old version. + exp-merge (Experimental) Merge upstream changes with local configs using git rebase. virtmon (For dev only) Create virtual monitors for testing multi-monitors. checkdeps (For dev only) Check whether pkgs exist in AUR or repos of Arch. @@ -48,7 +49,7 @@ case $1 in # Global help ""|help|--help|-h)showhelp_global;exit;; # Correct subcommand - install|exp-uninstall|exp-update|exp-update-old|resetfirstrun|checkdeps|virtmon) + install|exp-uninstall|exp-update|exp-update-old|exp-merge|resetfirstrun|checkdeps|virtmon) SUBCMD_NAME=$1 SUBCMD_DIR=./sdata/subcmd-$1 shift;;