Useful aliases

alias ll="ls -lah"
alias branch="git branch"
alias checkout="git checkout"
commit() {
  echo "Enter commit message (Ctrl+D to finish):"
  msg=$(cat)
  git commit -m "$msg"
}
add() {
  # Ensure git repo
  git rev-parse --is-inside-work-tree >/dev/null 2>&1 || return 0

  # Colors
  GREEN="\e[32m"
  RED="\e[31m"
  YELLOW="\e[33m"
  CYAN="\e[36m"
  BOLD="\e[1m"
  RESET="\e[0m"

  local current_branch remote_branch behind
  local files file answer
  local added_files=()
  local skipped_files=()
  local temp_ignored_files=()
  local existing_temp_ignored=()

  current_branch=$(git branch --show-current)

  # ---------- STEP 0: Check existing temp-ignore files ----------

  mapfile -t existing_temp_ignored < <(git ls-files -v | awk '/^S / {print substr($0,3)}')

  if [ ${#existing_temp_ignored[@]} -gt 0 ]; then
    echo -e "${YELLOW}⚠ There are temporarily ignored (skip-worktree) files:${RESET}"
    printf '  - %s\n' "${existing_temp_ignored[@]}"
    echo
    printf "${BOLD}Continue staging anyway?${RESET} [${GREEN}Y${RESET}=yes, ${RED}N${RESET}=no]: "
    read -r answer

    case "$answer" in
      y|Y)
        echo
        ;;
      *)
        echo -e "${RED}Staging aborted.${RESET}"
        return 0
        ;;
    esac
  fi

  # ---------- STEP 1: Remote update check ----------

  printf "${CYAN}Check updates from which branch?${RESET} [default: %s]: " "origin/$current_branch"
  read -r input_branch

  if [ -z "$input_branch" ]; then
    remote_branch="origin/$current_branch"
  else
    remote_branch="origin/$input_branch"
  fi

  echo "Fetching latest refs..."
  git fetch >/dev/null 2>&1 || {
    echo -e "${RED}Fetch failed.${RESET}"
    return 1
  }

  behind=$(git rev-list --count HEAD.."$remote_branch" 2>/dev/null)

  if [ -z "$behind" ]; then
    echo -e "${RED}Branch '$remote_branch' not found.${RESET}"
    return 1
  fi

  if [ "$behind" -gt 0 ]; then
    echo -e "${RED}✖ $remote_branch has $behind new commit(s).${RESET}"
    echo -e "${YELLOW}Staging is blocked to prevent conflicts.${RESET}"
    echo
    cat <<EOF
Suggested actions:
  git merge $remote_branch (preserves history)
  # or
  git rebase $remote_branch (rewrites history)
EOF
    echo
    echo -e "${BOLD}Resolve updates first, then re-run staging.${RESET}"
    return 1
  fi

  echo -e "${GREEN}✔ No updates in $remote_branch. Continuing to staging.${RESET}"

  # ---------- STEP 2: Interactive staging ----------

  files=$(git status --porcelain | awk '{print $2}')

  if [ -z "$files" ]; then
    echo -e "${YELLOW}No modified files to stage.${RESET}"
    return 0
  fi

  for file in $files; do
    while true; do
      printf "${BOLD}Add '${file}'?${RESET} [${GREEN}y${RESET}=yes, ${RED}n${RESET}=no, ${YELLOW}d${RESET}=diff, ${CYAN}t${RESET}=temp-ignore]: "
      read -r answer

      case "$answer" in
        y|Y)
          git add -- "$file"
          added_files+=("$file")
          break
          ;;
        n|N)
          skipped_files+=("$file")
          break
          ;;
        d|D)
          echo -e "${YELLOW}--- Diff for $file ---${RESET}"
          git diff -- "$file"
          echo -e "${YELLOW}----------------------${RESET}"
          ;;
        t|T)
          if git ls-files -v -- "$file" | grep -q '^S'; then
            echo -e "${YELLOW}Already temporarily ignored.${RESET}"
          else
            git update-index --skip-worktree -- "$file"
            echo -e "${CYAN}Temporarily ignored (skip-worktree).${RESET}"
          fi
          temp_ignored_files+=("$file")
          break
          ;;
        *)
          echo -e "${RED}Invalid input, choose y, n, d, or t.${RESET}"
          ;;
      esac
    done
  done

  # ---------- STEP 3: Summary ----------

  echo -e "\n${GREEN}Added files:${RESET}"
  [ ${#added_files[@]} -eq 0 ] && echo "(None)" || printf '%s\n' "${added_files[@]}"

  echo -e "\n${RED}Skipped files:${RESET}"
  [ ${#skipped_files[@]} -eq 0 ] && echo "(None)" || printf '%s\n' "${skipped_files[@]}"

  echo -e "\n${CYAN}Temporarily ignored (skip-worktree):${RESET}"
  [ ${#temp_ignored_files[@]} -eq 0 ] && echo "(None)" || printf '%s\n' "${temp_ignored_files[@]}"

  echo -e "\n${YELLOW}Done.${RESET}"
}
git_push() {
  # Colors
  RED="\033[0;31m"
  GREEN="\033[0;32m"
  CYAN="\033[0;36m"
  YELLOW="\033[1;33m"
  NC="\033[0m"

  CURRENT_BRANCH="$(git rev-parse --abbrev-ref HEAD)"

  git fetch origin "$CURRENT_BRANCH" >/dev/null 2>&1

  echo -e "${CYAN}Commits to push on $CURRENT_BRANCH:${NC}"
  git log origin/$CURRENT_BRANCH..$CURRENT_BRANCH --oneline || echo "No upstream set yet."
  echo

  BEHIND_COUNT=$(git rev-list --count $CURRENT_BRANCH..origin/$CURRENT_BRANCH 2>/dev/null || echo 0)

  if [ "$BEHIND_COUNT" -gt 0 ]; then
    echo -e "${YELLOW}⚠ Your branch is behind origin by $BEHIND_COUNT commit(s).${NC}"
    read -p "Auto rebase with 'git pull --rebase'? (Y/n): " REBASE_CONF
    REBASE_CONF=${REBASE_CONF:-Y}

    if [[ "$REBASE_CONF" =~ ^[Yy]$ ]]; then
      echo -e "${CYAN}Rebasing...${NC}"
      if ! git pull --rebase origin "$CURRENT_BRANCH"; then
        echo -e "${RED}❌ Rebase failed. Resolve conflicts and retry.${NC}"
        return 1
      fi
    else
      echo -e "${RED}Push cancelled because branch is behind.${NC}"
      return 1
    fi
  fi

  if [[ "$CURRENT_BRANCH" == "main" || "$CURRENT_BRANCH" == "master" ]]; then
    echo -e "${RED}⚠ You are on ${CURRENT_BRANCH}. Be careful!${NC}"
    read -p "Type YES to confirm push: " CONFIRM
    if [[ "$CONFIRM" != "YES" ]]; then
      echo -e "${YELLOW}Push cancelled.${NC}"
      return 1
    fi
  fi

  printf "Enter remote branch name (or press Enter to use %s): " "$CURRENT_BRANCH"
  read USER_BRANCH
  if [ -z "$USER_BRANCH" ]; then USER_BRANCH="$CURRENT_BRANCH"; fi

  echo -e "${GREEN}Pushing to origin/$USER_BRANCH ...${NC}"

  if git push -u origin "$CURRENT_BRANCH":"$USER_BRANCH"; then
    echo
    echo -e "${GREEN}✔ Push successful!${NC}"
    echo -e "${CYAN}📝 Summary of pushed commits:${NC}"

    # Show commits that just got pushed
    git log origin/$USER_BRANCH --oneline --no-decorate | head -n 10

    echo -e "${YELLOW}(showing latest up to 10 commits)${NC}"
    echo
    echo -e "${GREEN}Remote branch: origin/$USER_BRANCH${NC}"
  else
    echo -e "${RED}❌ Push failed.${NC}"
    return 1
  fi
}

alias push="git_push"
unstaged() {
  local files
  files=$(git diff --cached --name-only)

  if [ -z "$files" ]; then
    echo -e "\e[33mNo staged files.\e[0m"
    return 0
  fi

  # Colors
  GREEN="\e[32m"
  RED="\e[31m"
  YELLOW="\e[33m"
  CYAN="\e[36m"
  BOLD="\e[1m"
  RESET="\e[0m"

  echo -e "${BOLD}Staged files:${RESET}"
  echo "----------------"

  while IFS= read -r file; do
    echo
    echo -e "${CYAN}File:${RESET} ${BOLD}$file${RESET}"
    echo -e "${YELLOW}--- Staged diff ---${RESET}"
    git diff --cached -- "$file"

    echo
    printf "${BOLD}Action${RESET} [${GREEN}y${RESET}=unstage, ${RED}n${RESET}=skip, ${CYAN}a${RESET}=unstage ALL]: "
    read -r answer

    case "$answer" in
      y|Y)
        git restore --staged "$file"
        echo -e "${GREEN}✔ Unstaged:${RESET} $file"
        ;;
      a|A)
        git restore --staged .
        echo -e "${RED}✔ All files unstaged.${RESET}"
        return 0
        ;;
      *)
        echo -e "${YELLOW}⏭ Skipped:${RESET} $file"
        ;;
    esac
  done <<< "$files"

  echo
  echo -e "${BOLD}Done.${RESET}"
}
untemp() {
  # Ensure git repo
  git rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
    echo "Not a git repository."
    return 1
  }

  # Collect skip-worktree files
  mapfile -t files < <(git ls-files -v | awk '/^S / {print substr($0,3)}')

  if [ ${#files[@]} -eq 0 ]; then
    echo "No temporarily ignored (skip-worktree) files found."
    return 0
  fi

  echo "Removing temp-ignore (skip-worktree) from:"
  for file in "${files[@]}"; do
    echo "  - $file"
    git update-index --no-skip-worktree -- "$file"
  done

  echo
  echo "Restored files:"
  printf '%s\n' "${files[@]}"
}

Total page views:

Comments

Popular posts from this blog

Start all Docker containers