Does Git have its own way to canonicalize (resolve) symbolic links like readlink -f?

readlink -f, according to its page:

"canonicalize [s], following each symbolic link in each component of the given name recursively, only the last component must exist

I want a "readlink -f" equivalent for Git. In other words, a way to keep track of every symbolic link in the path at a given commit in the repository, ending with the canonical version of the path.

I wrote my own function that does this (below), but I would like to know if Git has built in functionality that does the same. And, if that's not the case, it might be worth considering including equivalent functionality in the new version of Git. Thank.

SYMLINK_FILE_TYPE_OCTAL="12"
MAX_SYMLINK_COUNT=10

git-is-symlink() {
  local commit="$1"
  local dir="$2"
  local base="$3"
  git ls-tree "${commit}:$dir" "$base" | grep -q -P "^$SYMLINK_FILE_TYPE_OCTAL"
}

git-get-symlink-target() {
  local commit="$1"
  local dir="$2"
  local base="$3"

  if test -z "$dir"; then
    local path="$base"
  else
    local path="$dir/$base"
  fi
  git cat-file blob "$commit:$path"
}

git-resolve-path-recursive() {
  local commit="$1"
  local resolved_prefix="$2"
  local segment_to_be_resolved="$3"
  local unresolved_suffix="$4"
  local symlink_count="$5"

  if test -z "$segment_to_be_resolved" && test -z "$unresolved_suffix"; then
    echo "$resolved_prefix"
    return
  fi

  if test "$symlink_count" -gt "$MAX_SYMLINK_COUNT"; then
    echo "Exceeded symlink count of $MAX_SYMLINK_COUNT on '$prefix' '$segment_to_be_resolved'" >&2
    exit 1
  fi

  if test -n "$segment_to_be_resolved" && git-is-symlink "$commit" "$resolved_prefix" "$segment_to_be_resolved"; then
    local symlink_target="$(git-get-symlink-target "$commit" "$resolved_prefix" "$segment_to_be_resolved")"
    git-resolve-path-recursive "$commit" "$resolved_prefix" "$symlink_target" "$unresolved_suffix" $((++symlink_count))
  else
    if test -z "$resolved_prefix"; then
      local new_resolved_prefix="$segment_to_be_resolved"
    else
      local new_resolved_prefix="$resolved_prefix/$segment_to_be_resolved"
    fi
    local new_segment_to_be_resolved="$(echo "$unresolved_suffix" | cut -d / -f 1)"
    local new_unresolved_suffix="$(echo "$unresolved_suffix" | cut -s -d / -f 2-)"
    git-resolve-path-recursive "$commit" "$new_resolved_prefix" "$new_segment_to_be_resolved" "$new_unresolved_suffix" 0
  fi
}

## Main entry point
## USAGE: git-resolve-path COMMIT PATH
git-resolve-path() {
  local commit="$1"
  local path="$2"
  git-resolve-path-recursive "$commit" "" "" "$path" 0
}

      

Usage example:

> mkdir -p targetdir
> ln -s targetdir linkdir
> touch targetdir/targetfile
> ln -s targetfile targetdir/linkfile
> git add targetdir linkdir targetdir/targetfile targetdir/linkfile
> git commit -m "Add files"
> git-resolve-path HEAD linkdir/linkfile
targetdir/targetfile

      

+3


source to share





All Articles