thoughtbot / rcm

rc file (dotfile) management
https://thoughtbot.github.io/rcm/rcm.7.html
BSD 3-Clause "New" or "Revised" License
3.12k stars 135 forks source link

optional .local suffix for colliding files from multiple sources #281

Closed qrkourier closed 1 year ago

qrkourier commented 3 years ago

I'm trying to avoid renaming the colliding dotfiles in my personal repo so that I can use them on systems where I'm not also using thoughtbotdotfiles with the .local convention. Collisions are ignored by rcup today, and here's how an rcup -L option could modify that behavior.

# two dotfiles with the same name exist in multiple sources
# ~/Sites/thoughtbotdotfiles/zshrc
# ~/Sites/qrkourier/dotfiles/zshrc
$ rcup -d ~/Sites/thoughtbotdotfiles -d ~/Sites/qrkourier/dotfiles -L
$ ls -l ~/.zshrc{,.local}
lrwxrwxrwx 1 qrkourier qrkourier 45 Sep 22 18:32 /home/qrkourier/.zshrc -> /home/qrkourier/Sites/thoughtbotdotfiles/zshrc
lrwxrwxrwx 1 qrkourier qrkourier 35 Sep 18 11:55 /home/qrkourier/.zshrc.local -> /home/qrkourier/Sites/dotfiles/zshrc
qrkourier commented 3 years ago

Though I'm unfamiliar, I noticed there were some additional env vars or configuration files or both that might serve to add more granularity to this new behavior. Even without additional controls a simple default to .local suffix when a name is already claimed by a prior source file would immediately make rcup more useful for me. A few years ago when I first encountered this limitation I wrote a shell function emulating rcup and implementing the .local suffix for collisions. I don't know why I did that instead of adding the option myself, but it does work well.

rcupl (){
  #
  # qrkourer: 2016
  #
  DEBUG=""
  test -d "$1" || { echo "Expects the link target dir as the first argument, bye"; return 1; }
  test -d "$2" || { echo "Expects the link location dir as the second argument, bye"; return 1; }
  # set the optional dotfile prefix accepting 'nil' to mean no prefix
  case "$3" in
    "") local RCPRE='.';;
 "nil") local RCPRE='';;
     *) local RCPRE="$3";;
  esac
  for RCTGT in $1/*;do
    [[ $RCTGT == $1/\* ]] && continue
    local RCLNK="$2/${RCPRE}$(basename $RCTGT)"
    # test whether the target is a directory
    if test -d "$RCTGT";then
      # make the corresponding link directory and a shortcut link sans prefix
      # (e.g., ./bin -> ./.bin)
      $DEBUG mkdir -pv "$RCLNK"
      $DEBUG ln -sTnv "$RCLNK" \
        "${RCLNK/\/$RCPRE$(basename $RCTGT)/\/$(basename $RCTGT)}" 2>/dev/null
    fi
    # test whether another file or intact link already exists
    if test -e "$RCLNK"; then
      # test whether the existing file is a link
      if test -L "$RCLNK";then
        # test whether the existing link already points to the intended target
        if [[ "$(readlink -f $RCTGT)" == "$(readlink -f $RCLNK)" ]]; then
          # skip to next RCTGT
          continue
        else
      # if a link pointing elsewhere then assume it is a thoughtbot dotfile
      # and create the expected local include variant
          RCLNK="${RCLNK}.local"
          $DEBUG ln -sfnv "$RCTGT" "$RCLNK"
        fi
      # test whether the existing file is a directory
      elif test -d "$RCLNK";then
        # recurse
        rcupl "$RCTGT" "$RCLNK" nil
      # if a regular file then warn
      else
        echo "not clobbering existing regular file $RCLNK" >&2
      fi
    else
      # if not exists or broken link then create the normal link
      $DEBUG ln -sfnv "$RCTGT" "$RCLNK"
    fi
  done
}
qrkourier commented 3 years ago

I got so far as to realize that rcup consumes the list of managed files from lsrc which omits collisions. One way to handle this would be for lsrc -L -F to instead signify collisions local overrides with sigil L. That way, rcup could handle the local override with a ${dest}.local.

Another way of identifying local overrides in the Thoughtbot convention could be to use a reserved tag for this purpose like "local". Dotfiles with the special tag could always receive the .local suffix when a collision occurs.

mat-m commented 2 years ago

Hello @qrkourier ,

My raw thoughts: You only have two dotfiles repos, but some people have more, and rcm will not be able to suggest other extensions if there is more than one collision for a file. And what to do if we have a file and a folder with the same name ? Moreover, it would mean that the order of the paths is important, and I am not sure it is in the specs. Your suggestion about the local tag may be an option, but that does not scale,

My afterthought: rcm may need a spec on how to handle collisions. Current specs seems to be ''last one wins''. Can you try to elaborate a spec with what would happen in the following situations (at least):

Thank you

qrkourier commented 1 year ago

Thank you!