Skip to content

Commit

Permalink
Merge pull request #119 from dougborg/faster_link
Browse files Browse the repository at this point in the history
Updates for a faster link command.
  • Loading branch information
andsens committed Oct 14, 2014
2 parents 6b0c697 + 8a91d62 commit 50f9d8c
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 40 deletions.
84 changes: 52 additions & 32 deletions lib/commands/link.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,13 @@ function symlink {
ignore 'ignored' "$castle"
return $EX_SUCCESS
fi
oldIFS=$IFS
IFS=$'\n'
for filename in $(get_repo_files $repo/home); do
for filename in $(get_repo_files "$repo/home"); do
remote="$repo/home/$filename"
IFS=$oldIFS
local=$HOME/$filename
local="$HOME/$filename"

if [[ -e $local || -L $local ]]; then
# $local exists (but may be a dead symlink)
if [[ -L $local && $(readlink "$local") == $remote ]]; then
if [[ -L $local && $(readlink "$local") == "$remote" ]]; then
# $local symlinks to $remote.
if [[ -d $remote && ! -L $remote ]]; then
# If $remote is a directory -> legacy handling.
Expand Down Expand Up @@ -64,46 +61,69 @@ function symlink {
return $EX_SUCCESS
}

function get_repo_dirs {
# Loop through the files tracked by git and compute
# a list of their parent directories.
# The root of repo we are looking at, will not change.
local root=$1
# Check if this is the root invocation.
if [[ -n $2 ]]; then
# The relative path to the submodule:
relpath="$2/"
else
# First invocation, the repo_dir is just the root.
local repo_dir=$root
local relpath=''
fi
(
local path
while read path; do
printf "%s\n" "$relpath$path"
# Get all directory paths up to the root.
# We won't ever hit '/' here since ls-files
# always shows paths relative to the repo root.
while [[ $path =~ '/' ]]; do
path=$(dirname "$path")
printf "%s\n" "$relpath$path"
done
done < <(cd "$repo_dir" && git ls-files | xargs -I{} dirname "{}" | sort | uniq)
) | sort | uniq
}

function get_repo_files {
# This function descends recursively through all submodules
# of a repository and makes the paths returned by `git ls-files`
# relative to the root repo.
# All directory paths are computed as well.

# The root of repo we are looking at, will not change.
local root=$1
# Check if this is the root invocation
# Check if this is the root invocation.
if [[ -n $2 ]]; then
# The path to the current repo we are looking at
# The path to the current repo we are looking at:
repo_dir="$root/$2"
# The relative path to the submodule
# The relative path to the submodule:
relpath="$2/"
else
# First invocation, the repo dir is just the root
# First invocation, the repo_dir is just the root.
local repo_dir=$root
local relpath=''
fi
local paths=""
# Loop through the files tracked by git and compute
# a list of their parent directories.
for path in $(cd $repo_dir && git ls-files); do
# Don't add a newline to the beginning of the list
[[ -n $paths ]] && paths="$paths\n"
paths="$paths$relpath$path"
# Get all directory paths up to the root.
# We won't ever hit '/' here since ls-files
# always shows paths relative to the repo root.
while [[ $path =~ '/' ]]; do
path=$(dirname $path)
paths="$relpath$path\n$paths"
done
done
pending "list files" "$repo_dir" >&2
(
# Get files (+ submodule path prefix).
local path
while read path; do
printf "%s\n" "$relpath$path"
done < <(cd "$repo_dir" && git ls-files)

# Loop through all submodule children (not descendants, i.e. immediate children)
# and invoke the function again, passing the relative path to the submodule as the 2nd arg
for submodule in $(cd $repo_dir; git submodule --quiet foreach 'printf "%s\n" "$path"'); do
paths="$(get_repo_files $root $relpath$submodule)\n$paths"
done
# Get all directories (+ submodule path prefix).
get_repo_dirs "$root" "$relpath"

printf "$paths" | sort | uniq
# Recurse on all submodule children (not descendants, i.e. immediate
# children), passing the relative path to the submodule as the 2nd arg.
for submodule in $(cd "$repo_dir"; git submodule --quiet foreach 'printf "%s\n" "$path"'); do
get_repo_files "$root" "$relpath$submodule"
done
) | sort | uniq
success "list files" "$repo_dir" >&2
}
15 changes: 7 additions & 8 deletions test/suites/link.bats
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,16 @@ EOF
castle 'dotfiles'
mkdir -p $HOME/.config/bar.dir
cat > $HOME/.config/foo.conf <<EOF
#I am just a regular foo.conf file
#I am just a regular foo.conf file
[foo]
A=True
EOF
cat > $HOME/.config/bar.dir/bar.conf <<EOF
#I am just a regular bar.conf file
#I am just a regular bar.conf file
[bar]
A=True
EOF

[ -f "$HOME/.config/foo.conf" ]
#.config/foo.conf should be overwritten by a directory of the same name
[ -d "$HOME/.config/bar.dir" ]
Expand Down Expand Up @@ -161,14 +161,13 @@ EOF

@test 'fail when linking file with newline' {
castle 'rc-files'
touch "$HOMESICK/repos/rc-files/home/filename
test_filename="filename
newline"
touch "$HOMESICK/repos/rc-files/home/$test_filename"
commit_repo_state $HOMESICK/repos/rc-files
$HOMESHICK_FN --batch link rc-files
[ -L "$HOME/\"filename" ]
[ -L "$HOME/newline\"" ]
is_symlink $HOMESICK/repos/rc-files/home/\"filename $HOME/\"filename
is_symlink $HOMESICK/repos/rc-files/home/newline\" $HOME/newline\"
[ -L "$HOME/\"filenamennewline\"" ]
is_symlink "$HOMESICK/repos/rc-files/home/\"filenamennewline\"" "$HOME/\"filenamennewline\""
}

@test 'files ignored by git should not be linked' {
Expand Down

0 comments on commit 50f9d8c

Please sign in to comment.