git.fiddlerwoaroof.com
Raw Blame History
#!/usr/bin/env zsh

set -e -u -o pipefail

zmodload -F zsh/stat b:zstat
abspath() {
  if [ -L "$1" ]; then
    local link
    zstat -A link +link -- "$1" || return
    case $link in
      (/*) ;;
      (*)
        case $1:h in
          (*/) link=$1:h$link;;
          (*) link=$1:h/$link;;
        esac;;
    esac
    printf '%s\n' $link:h:A/$link:t
  else
    printf '%s\n' $1:A
  fi
}

linkdir=$(abspath "${1?need a link directory}")/
srcdir=$(abspath "${2?need a source directory}")/
shift
shift

echo deduplicating "$srcdir" to "$linkdir"

get_target_directory() {
  first_level=${1[1,2]}
  second_level=${1[1,5]}
  echo "$first_level"/"$second_level"
}

get_target_path() {
  dir="$(get_target_directory "$1")"
  mkdir -p "$linkdir"/"$dir"
  echo "$linkdir"/"$dir"/"$1"
}

file_to_hashed_dir() {
  hash="$(sha256sum "$1" | awk '{print $1}')"
  get_target_path "$hash"
}

linkify_tree() {
  cd "$1"
  find . -type f | {
    while read -r fn; do
      target="$(file_to_hashed_dir "$fn")"
      if ! [[ -L "$fn" ]]; then
        if ! [[ -f "$target" ]]; then
          if [[ -e "$target" ]]; then
            echo $target is not a normal file
            ls -l "$target"
            exit 1
          fi
          mv "$fn" "$target"
        else
          mv "$fn" "$fn".old
        fi
        ln -v "$target" "$fn"
        if [[ -f "$fn".old ]]; then
          rm "$fn".old
        fi
      else
        touch $target
      fi
      echo
    done
  }
}

linkify_tree "$srcdir"