-
Notifications
You must be signed in to change notification settings - Fork 145
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix pull not symlinking new and renamed files
pull creates a temporary tag in each castle before running git pull, and uses it to determine which files have been added (and renamed) in symlink_new_files, which also deletes it.
- Loading branch information
Showing
5 changed files
with
323 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
#!/usr/bin/env bash | ||
|
||
# shellcheck disable=2164 | ||
fixture_pull_renamed() { | ||
local git_username="Homeshick user" | ||
local git_useremail="[email protected]" | ||
local repo="$REPO_FIXTURES/pull-renamed" | ||
git init "$repo" | ||
cd "$repo" | ||
git config user.name "$git_username" | ||
git config user.email "$git_useremail" | ||
mkdir home | ||
cd home | ||
|
||
cat > .bashrc-wrong-name <<EOF | ||
#!/usr/bin/env bash | ||
PS1='\[33[01;32m\]\u@\h\[33[00m\]:\[33[01;34m\]\w\' | ||
EOF | ||
git add .bashrc-wrong-name | ||
git commit -m '.bashrc file for my new repo' | ||
|
||
git mv .bashrc-wrong-name .bashrc | ||
git commit -m 'fixed .bashrc file name' | ||
|
||
cat >> .bashrc <<EOF | ||
export IMPORTANT_VARIABLE=1 | ||
EOF | ||
git add .bashrc | ||
git commit -m 'Modified .bashrc to set IMPORTANT_VARIABLE' | ||
} | ||
|
||
fixture_pull_renamed > /dev/null |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#!/usr/bin/env bash | ||
|
||
# shellcheck disable=2164 | ||
fixture_pull_test() { | ||
local git_username="Homeshick user" | ||
local git_useremail="[email protected]" | ||
local repo="$REPO_FIXTURES/pull-test" | ||
git init "$repo" | ||
cd "$repo" | ||
git config user.name "$git_username" | ||
git config user.email "$git_useremail" | ||
mkdir home | ||
cd home | ||
|
||
cat > .bashrc <<EOF | ||
#!/usr/bin/env bash | ||
PS1='\[33[01;32m\]\u@\h\[33[00m\]:\[33[01;34m\]\w\' | ||
EOF | ||
git add .bashrc | ||
git commit -m '.bashrc file for my new pull-test repo' | ||
|
||
cat > .gitignore <<EOF | ||
.DS_Store | ||
*.swp | ||
EOF | ||
git add .gitignore | ||
git commit -m 'Added .gitignore file' | ||
|
||
cat >> .bashrc <<EOF | ||
export IMPORTANT_VARIABLE=1 | ||
EOF | ||
git add .bashrc | ||
git commit -m 'Modified .bashrc to set IMPORTANT_VARIABLE' | ||
} | ||
|
||
fixture_pull_test > /dev/null |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,92 @@ teardown() { | |
delete_test_dir | ||
} | ||
|
||
BEFORE_PULL_TAG=__homeshick-before-pull__ | ||
assert_tag_is_removed() { | ||
local castle | ||
for castle in "$@"; do | ||
( | ||
cd "$HOME/.homesick/repos/$castle" || return $? | ||
# show all the tags if the test fails | ||
{ | ||
echo "all tags in $castle:" | ||
git show-ref --tags || true | ||
echo | ||
} >&2 | ||
# this tag should not exist | ||
run git rev-parse --verify "refs/tags/$BEFORE_PULL_TAG" >&2 2>&- | ||
assert_failure | ||
) | ||
done | ||
} | ||
|
||
assert_tag_points_to() { | ||
( | ||
castle="$1" | ||
tag_before="$2" | ||
cd "$HOME/.homesick/repos/$castle" || return $? | ||
# show all the tags if the test fails | ||
{ | ||
echo "all tags in $castle:" | ||
git show-ref --tags || true | ||
echo | ||
} >&2 | ||
tag_after=$(git rev-parse "refs/tags/$BEFORE_PULL_TAG") | ||
[ "$tag_before" == "$tag_after" ] | ||
) | ||
} | ||
|
||
reset_and_add_new_file() { | ||
# takes castle name as first argument and commit to reset to as second | ||
cd "$HOME/.homesick/repos/$1" || return $? | ||
git reset --hard "$2" >/dev/null | ||
|
||
git config user.name "Homeshick user" | ||
git config user.email "[email protected]" | ||
|
||
cat > home/.ignore <<EOF | ||
.DS_Store | ||
*.swp | ||
EOF | ||
git add home/.ignore >/dev/null | ||
git commit -m 'Added .ignore file' >/dev/null | ||
} | ||
|
||
expect_new_files() { | ||
# takes castle name as first argument, and new files as remaining arguments | ||
local castle="$1" | ||
shift | ||
local green='\e[1;32m' | ||
local cyan='\e[1;36m' | ||
local white='\e[1;37m' | ||
local reset='\e[0m' | ||
# these variables are intended to be parsed by printf | ||
# shellcheck disable=SC2059 | ||
{ | ||
printf "$cyan pull$reset %s\r" "$castle" | ||
printf "$green pull$reset %s\n" "$castle" | ||
printf "$white updates$reset The castle %s has new files.\n" "$castle" | ||
printf "$cyan symlink?$reset [yN] y\r" | ||
printf "$green symlink?$reset [yN] \n" | ||
for file in "$@"; do | ||
printf "$cyan symlink$reset %s\r" "$file" | ||
printf "$green symlink$reset %s\n" "$file" | ||
done | ||
} | assert_output - | ||
} | ||
|
||
expect_no_new_files() { | ||
# takes castle name as first argument | ||
local castle="$1" | ||
local green='\e[1;32m' | ||
local cyan='\e[1;36m' | ||
local reset='\e[0m' | ||
{ | ||
printf "$cyan pull$reset %s\r" "$castle" | ||
printf "$green pull$reset %s\n" "$castle" | ||
} | assert_output - | ||
} | ||
|
||
@test 'pull skips castles with no upstream remote' { | ||
castle 'rc-files' | ||
castle 'dotfiles' | ||
|
@@ -20,6 +106,157 @@ teardown() { | |
(cd "$HOME/.homesick/repos/rc-files" && git remote rm origin) | ||
run homeshick pull rc-files dotfiles | ||
[ $status -eq 0 ] # EX_SUCCESS | ||
assert_tag_is_removed rc-files dotfiles | ||
# dotfiles FETCH_HEAD should exist if the castle was pulled | ||
[ -e "$HOME/.homesick/repos/dotfiles/.git/FETCH_HEAD" ] | ||
} | ||
|
||
@test 'pull prompts for symlinking if new files are present' { | ||
castle 'rc-files' | ||
(cd "$HOME/.homesick/repos/rc-files" && git reset --hard HEAD~1 >/dev/null) | ||
homeshick link --batch --quiet rc-files | ||
|
||
[ ! -e "$HOME/.gitignore" ] | ||
run homeshick pull rc-files <<<y | ||
assert_success | ||
assert_tag_is_removed rc-files | ||
expect_new_files rc-files .gitignore | ||
[ -f "$HOME/.gitignore" ] | ||
} | ||
|
||
@test 'pull prompts for symlinking with renamed files' { | ||
castle 'pull-renamed' | ||
# reset to before .bashrc-wrong-name was renamed to .bashrc | ||
(cd "$HOME/.homesick/repos/pull-renamed" && git reset --hard HEAD~2 >/dev/null) | ||
|
||
[ ! -e "$HOME/.bashrc" ] | ||
run homeshick pull pull-renamed <<<y | ||
assert_success | ||
assert_tag_is_removed pull-renamed | ||
expect_new_files pull-renamed .bashrc | ||
[ -f "$HOME/.bashrc" ] | ||
} | ||
|
||
@test 'pull with no new files present' { | ||
castle 'pull-test' | ||
(cd "$HOME/.homesick/repos/pull-test" && git reset --hard HEAD~1 >/dev/null) | ||
|
||
run homeshick pull --batch pull-test | ||
assert_success | ||
assert_tag_is_removed pull-test | ||
expect_no_new_files pull-test | ||
} | ||
|
||
@test 'pull a recently-pulled castle again' { | ||
# this checks that we don't try to link files again if the last operation was | ||
# a pull | ||
castle 'rc-files' | ||
(cd "$HOME/.homesick/repos/rc-files" && git reset --hard HEAD~1 >/dev/null) | ||
homeshick pull --batch --force rc-files | ||
|
||
run homeshick pull --batch rc-files | ||
assert_success | ||
assert_tag_is_removed rc-files | ||
expect_no_new_files rc-files | ||
} | ||
|
||
@test 'pull a castle with a git conflict' { | ||
local castle=pull-test | ||
castle 'pull-test' | ||
(reset_and_add_new_file pull-test HEAD~2) | ||
(cd "$HOME/.homesick/repos/pull-test" && git config pull.rebase false && git config pull.ff only) | ||
|
||
[ ! -e "$HOME/.gitignore" ] | ||
run homeshick pull --batch pull-test | ||
assert_failure 70 # EX_SOFTWARE | ||
assert_tag_is_removed pull-test | ||
[ ! -e "$HOME/.gitignore" ] | ||
local red='\e[1;31m' | ||
local cyan='\e[1;36m' | ||
local reset='\e[0m' | ||
{ | ||
echo -ne "$cyan pull$reset pull-test\r" | ||
echo -ne "$red pull$reset pull-test\n" | ||
echo -ne "$red error$reset Unable to pull $HOME/.homesick/repos/pull-test. Git says:\n" | ||
} | assert_output -p - | ||
# possibly some git advice | ||
assert_output -p "fatal: Not possible to fast-forward, aborting." | ||
} | ||
|
||
@test 'pull cleans up any marker tags after a git error' { | ||
castle 'dotfiles' | ||
castle 'pull-test' | ||
castle 'nodirs' | ||
(reset_and_add_new_file pull-test HEAD~2) | ||
(cd "$HOME/.homesick/repos/pull-test" && git config pull.rebase false && git config pull.ff only) | ||
|
||
run homeshick pull --batch dotfiles pull-test nodirs | ||
assert_failure 70 # EX_SOFTWARE | ||
# tags in all castles should be cleaned up | ||
assert_tag_is_removed dotfiles pull-test nodirs | ||
local green='\e[1;32m' | ||
local red='\e[1;31m' | ||
local cyan='\e[1;36m' | ||
local reset='\e[0m' | ||
{ | ||
echo -ne "$cyan pull$reset dotfiles\r" | ||
echo -ne "$green pull$reset dotfiles\n" | ||
echo -ne "$cyan pull$reset pull-test\r" | ||
echo -ne "$red pull$reset pull-test\n" | ||
echo -ne "$red error$reset Unable to pull $HOME/.homesick/repos/pull-test. Git says:\n" | ||
} | assert_output -p - | ||
# possibly some git advice | ||
{ | ||
echo "fatal: Not possible to fast-forward, aborting." | ||
echo -ne "$cyan pull$reset nodirs\r" | ||
echo -ne "$green pull$reset nodirs\n" | ||
} | assert_output -p - | ||
} | ||
|
||
@test 'pull a castle where the marker tag already exists' { | ||
castle 'rc-files' | ||
local tag_before | ||
tag_before=$( | ||
cd "$HOME/.homesick/repos/rc-files" && | ||
git reset --hard HEAD~1 >/dev/null && | ||
git tag "$BEFORE_PULL_TAG" HEAD^ && | ||
git rev-parse "$BEFORE_PULL_TAG" | ||
) | ||
|
||
[ ! -e "$HOME/.gitignore" ] | ||
run homeshick pull --batch rc-files | ||
assert_failure 65 # EX_DATAERR | ||
# tag should not be touched | ||
assert_tag_points_to rc-files "$tag_before" | ||
[ ! -e "$HOME/.gitignore" ] | ||
|
||
local red='\e[1;31m' | ||
local cyan='\e[1;36m' | ||
local reset='\e[0m' | ||
{ | ||
echo -ne "$cyan pull$reset rc-files\r" | ||
echo -ne "$red pull$reset rc-files\n" | ||
echo -ne "$red error$reset Pull marker tag ($BEFORE_PULL_TAG) already exists in $HOME/.homesick/repos/rc-files. Please resolve this before pulling." | ||
} | assert_output - | ||
} | ||
|
||
@test 'pull only cleans up the marker tag if we created it' { | ||
castle 'dotfiles' | ||
castle 'rc-files' | ||
local tag_before | ||
tag_before=$( | ||
cd "$HOME/.homesick/repos/rc-files" && | ||
git reset --hard HEAD~1 >/dev/null && | ||
git tag "$BEFORE_PULL_TAG" HEAD^ && | ||
git rev-parse "refs/tags/$BEFORE_PULL_TAG" | ||
) | ||
|
||
[ ! -e "$HOME/.gitignore" ] | ||
run homeshick pull --batch dotfiles rc-files | ||
assert_failure 65 # EX_DATAERR | ||
# tag in dotfiles should be cleaned up | ||
assert_tag_is_removed dotfiles | ||
# tag in rc-files should not be touched | ||
assert_tag_points_to rc-files "$tag_before" | ||
[ ! -e "$HOME/.gitignore" ] | ||
} |