commit - b448fd00850830156870881effc6cafd6233b8b6
commit + fa3cef63799016195e8a917f39c82815522692aa
blob - fc7b322b0c224ff327dbe3d499fab10f2c8c7c96
blob + df57239f50ac1caa3457f59d6df054cab52a05bc
--- lib/worktree.c
+++ lib/worktree.c
struct got_blob_object *blob = NULL;
FILE *f1 = NULL, *f2 = NULL, *outfile = NULL;
int fd2 = -1;
+ char link_target[PATH_MAX];
+ ssize_t link_len = 0;
char *path1 = NULL, *id_str = NULL;
struct stat sb1, sb2;
struct got_diff_changes *changes = NULL;
if (dirfd2 != -1) {
fd2 = openat(dirfd2, de_name2, O_RDONLY | O_NOFOLLOW);
if (fd2 == -1) {
- err = got_error_from_errno2("openat", path2);
- goto done;
+ if (errno != ELOOP) {
+ err = got_error_from_errno2("openat", path2);
+ goto done;
+ }
+ link_len = readlinkat(dirfd2, de_name2,
+ link_target, sizeof(link_target));
+ if (link_len == -1)
+ return got_error_from_errno2("readlinkat", path2);
+ sb2.st_mode = S_IFLNK;
+ sb2.st_size = link_len;
}
} else {
fd2 = open(path2, O_RDONLY | O_NOFOLLOW);
if (fd2 == -1) {
- err = got_error_from_errno2("open", path2);
- goto done;
+ if (errno != ELOOP) {
+ err = got_error_from_errno2("open", path2);
+ goto done;
+ }
+ link_len = readlink(path2, link_target,
+ sizeof(link_target));
+ if (link_len == -1)
+ return got_error_from_errno2("readlink", path2);
+ sb2.st_mode = S_IFLNK;
+ sb2.st_size = link_len;
}
}
- if (fstat(fd2, &sb2) == -1) {
- err = got_error_from_errno2("fstat", path2);
- goto done;
- }
+ if (fd2 != -1) {
+ if (fstat(fd2, &sb2) == -1) {
+ err = got_error_from_errno2("fstat", path2);
+ goto done;
+ }
- f2 = fdopen(fd2, "r");
- if (f2 == NULL) {
- err = got_error_from_errno2("fdopen", path2);
- goto done;
+ f2 = fdopen(fd2, "r");
+ if (f2 == NULL) {
+ err = got_error_from_errno2("fdopen", path2);
+ goto done;
+ }
+ fd2 = -1;
+ } else {
+ size_t n;
+ f2 = got_opentemp();
+ if (f2 == NULL) {
+ err = got_error_from_errno2("got_opentemp", path2);
+ goto done;
+ }
+ n = fwrite(link_target, 1, link_len, f2);
+ if (n != link_len) {
+ err = got_ferror(f2, GOT_ERR_IO);
+ goto done;
+ }
+ if (fflush(f2) == EOF) {
+ err = got_error_from_errno("fflush");
+ goto done;
+ }
+ rewind(f2);
}
- fd2 = -1;
err = got_object_open_as_blob(&blob, repo, blob_id, 8192);
if (err)
if (err)
goto done;
- if (chmod(*path_outfile, sb2.st_mode) == -1) {
- err = got_error_from_errno2("chmod", path2);
- goto done;
+ if (!S_ISLNK(sb2.st_mode)) {
+ if (chmod(*path_outfile, sb2.st_mode) == -1) {
+ err = got_error_from_errno2("chmod", path2);
+ goto done;
+ }
}
}
done:
blob - cf4a6d0c34ede0224a4b154ee7f65844ddf9f51a
blob + 937cb0bffe198932b2348672a1f25bf74819b672
--- regress/cmdline/stage.sh
+++ regress/cmdline/stage.sh
fi
echo -n ".got/bar" > $testroot/content.expected
cp $testroot/wt/dotgotbar.link $testroot/content
+ cmp -s $testroot/content.expected $testroot/content
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/content.expected $testroot/content
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ if [ -h $testroot/wt/dotgotfoo.link ]; then
+ echo "dotgotfoo.link is a symlink"
+ test_done "$testroot" "1"
+ return 1
+ fi
+ echo "this is regular file foo" > $testroot/content.expected
+ cp $testroot/wt/dotgotfoo.link $testroot/content
+ cmp -s $testroot/content.expected $testroot/content
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/content.expected $testroot/content
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ if ! [ -h $testroot/wt/epsilon.link ]; then
+ echo "epsilon.link is not a symlink"
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ readlink $testroot/wt/epsilon.link > $testroot/stdout
+ echo "gamma" > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ if [ -h $testroot/wt/passwd.link ]; then
+ echo "passwd.link is a symlink"
+ test_done "$testroot" "1"
+ return 1
+ fi
+ echo -n "/etc/passwd" > $testroot/content.expected
+ cp $testroot/wt/passwd.link $testroot/content
+ cmp -s $testroot/content.expected $testroot/content
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/content.expected $testroot/content
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ if ! [ -h $testroot/wt/zeta.link ]; then
+ echo "zeta.link is not a symlink"
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ readlink $testroot/wt/zeta.link > $testroot/stdout
+ echo "gamma/delta" > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ test_done "$testroot" "0"
+}
+
+function test_stage_patch_symlink {
+ local testroot=`test_init stage_patch_symlink`
+
+ (cd $testroot/repo && ln -s alpha alpha.link)
+ (cd $testroot/repo && ln -s epsilon epsilon.link)
+ (cd $testroot/repo && ln -s /etc/passwd passwd.link)
+ (cd $testroot/repo && ln -s ../beta epsilon/beta.link)
+ (cd $testroot/repo && ln -s nonexistent nonexistent.link)
+ (cd $testroot/repo && git add .)
+ git_commit $testroot/repo -m "add symlinks"
+ local head_commit=`git_show_head $testroot/repo`
+
+ got checkout $testroot/repo $testroot/wt > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ (cd $testroot/wt && ln -sf beta alpha.link)
+ (cd $testroot/wt && ln -sfh gamma epsilon.link)
+ (cd $testroot/wt && ln -sf ../gamma/delta epsilon/beta.link)
+ echo 'this is regular file foo' > $testroot/wt/dotgotfoo.link
+ (cd $testroot/wt && got add dotgotfoo.link > /dev/null)
+ (cd $testroot/wt && ln -sf .got/bar dotgotbar.link)
+ (cd $testroot/wt && got add dotgotbar.link > /dev/null)
+ (cd $testroot/wt && got rm nonexistent.link > /dev/null)
+ (cd $testroot/wt && ln -sf gamma/delta zeta.link)
+ (cd $testroot/wt && got add zeta.link > /dev/null)
+
+ printf "y\nn\ny\nn\ny\ny\ny" > $testroot/patchscript
+ (cd $testroot/wt && got stage -F $testroot/patchscript -p \
+ > $testroot/stdout)
+
+ cat > $testroot/stdout.expected <<EOF
+-----------------------------------------------
+@@ -1 +1 @@
+-alpha
+\ No newline at end of file
++beta
+\ No newline at end of file
+-----------------------------------------------
+M alpha.link (change 1 of 1)
+stage this change? [y/n/q] y
+A dotgotbar.link
+stage this addition? [y/n] n
+A dotgotfoo.link
+stage this addition? [y/n] y
+-----------------------------------------------
+@@ -1 +1 @@
+-../beta
+\ No newline at end of file
++../gamma/delta
+\ No newline at end of file
+-----------------------------------------------
+M epsilon/beta.link (change 1 of 1)
+stage this change? [y/n/q] n
+-----------------------------------------------
+@@ -1 +1 @@
+-epsilon
+\ No newline at end of file
++gamma
+\ No newline at end of file
+-----------------------------------------------
+M epsilon.link (change 1 of 1)
+stage this change? [y/n/q] y
+D nonexistent.link
+stage this deletion? [y/n] y
+A zeta.link
+stage this addition? [y/n] y
+EOF
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ rm $testroot/wt/alpha.link
+ echo 'this is regular file alpha.link' > $testroot/wt/alpha.link
+
+ (cd $testroot/wt && got diff -s > $testroot/stdout)
+
+ echo "diff $head_commit $testroot/wt (staged changes)" \
+ > $testroot/stdout.expected
+ echo -n 'blob - ' >> $testroot/stdout.expected
+ got tree -r $testroot/repo -i | grep 'alpha.link@ -> alpha$' | \
+ cut -d' ' -f 1 >> $testroot/stdout.expected
+ echo -n 'blob + ' >> $testroot/stdout.expected
+ (cd $testroot/wt && got stage -l alpha.link) | cut -d' ' -f 1 \
+ >> $testroot/stdout.expected
+ echo '--- alpha.link' >> $testroot/stdout.expected
+ echo '+++ alpha.link' >> $testroot/stdout.expected
+ echo '@@ -1 +1 @@' >> $testroot/stdout.expected
+ echo '-alpha' >> $testroot/stdout.expected
+ echo '\ No newline at end of file' >> $testroot/stdout.expected
+ echo '+beta' >> $testroot/stdout.expected
+ echo '\ No newline at end of file' >> $testroot/stdout.expected
+ echo 'blob - /dev/null' >> $testroot/stdout.expected
+ echo -n 'blob + ' >> $testroot/stdout.expected
+ (cd $testroot/wt && got stage -l dotgotfoo.link) | cut -d' ' -f 1 \
+ >> $testroot/stdout.expected
+ echo '--- /dev/null' >> $testroot/stdout.expected
+ echo '+++ dotgotfoo.link' >> $testroot/stdout.expected
+ echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected
+ echo '+this is regular file foo' >> $testroot/stdout.expected
+ echo -n 'blob - ' >> $testroot/stdout.expected
+ got tree -r $testroot/repo -i | grep 'epsilon.link@ -> epsilon$' | \
+ cut -d' ' -f 1 >> $testroot/stdout.expected
+ echo -n 'blob + ' >> $testroot/stdout.expected
+ (cd $testroot/wt && got stage -l epsilon.link) | cut -d' ' -f 1 \
+ >> $testroot/stdout.expected
+ echo '--- epsilon.link' >> $testroot/stdout.expected
+ echo '+++ epsilon.link' >> $testroot/stdout.expected
+ echo '@@ -1 +1 @@' >> $testroot/stdout.expected
+ echo '-epsilon' >> $testroot/stdout.expected
+ echo '\ No newline at end of file' >> $testroot/stdout.expected
+ echo '+gamma' >> $testroot/stdout.expected
+ echo '\ No newline at end of file' >> $testroot/stdout.expected
+ echo -n 'blob - ' >> $testroot/stdout.expected
+ got tree -r $testroot/repo -i | grep 'nonexistent.link@ -> nonexistent$' | \
+ cut -d' ' -f 1 >> $testroot/stdout.expected
+ echo 'blob + /dev/null' >> $testroot/stdout.expected
+ echo '--- nonexistent.link' >> $testroot/stdout.expected
+ echo '+++ /dev/null' >> $testroot/stdout.expected
+ echo '@@ -1 +0,0 @@' >> $testroot/stdout.expected
+ echo '-nonexistent' >> $testroot/stdout.expected
+ echo '\ No newline at end of file' >> $testroot/stdout.expected
+ echo 'blob - /dev/null' >> $testroot/stdout.expected
+ echo -n 'blob + ' >> $testroot/stdout.expected
+ (cd $testroot/wt && got stage -l zeta.link) | cut -d' ' -f 1 \
+ >> $testroot/stdout.expected
+ echo '--- /dev/null' >> $testroot/stdout.expected
+ echo '+++ zeta.link' >> $testroot/stdout.expected
+ echo '@@ -0,0 +1 @@' >> $testroot/stdout.expected
+ echo '+gamma/delta' >> $testroot/stdout.expected
+ echo '\ No newline at end of file' >> $testroot/stdout.expected
+
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ (cd $testroot/wt && got commit -m "staged symlink" \
+ > $testroot/stdout)
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ echo "got commit command failed unexpectedly" >&2
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ local commit_id=`git_show_head $testroot/repo`
+ echo "A dotgotfoo.link" > $testroot/stdout.expected
+ echo "A zeta.link" >> $testroot/stdout.expected
+ echo "M alpha.link" >> $testroot/stdout.expected
+ echo "M epsilon.link" >> $testroot/stdout.expected
+ echo "D nonexistent.link" >> $testroot/stdout.expected
+ echo "Created commit $commit_id" >> $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ got tree -r $testroot/repo -c $commit_id > $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ echo "got tree command failed unexpectedly" >&2
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ cat > $testroot/stdout.expected <<EOF
+alpha
+alpha.link@ -> beta
+beta
+dotgotfoo.link
+epsilon/
+epsilon.link@ -> gamma
+gamma/
+passwd.link@ -> /etc/passwd
+zeta.link@ -> gamma/delta
+EOF
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ return 1
+ fi
+
+ if [ -h $testroot/wt/alpha.link ]; then
+ echo "alpha.link is a symlink"
+ test_done "$testroot" "1"
+ return 1
+ fi
+
+ echo 'this is regular file alpha.link' > $testroot/content.expected
+ cp $testroot/wt/alpha.link $testroot/content
cmp -s $testroot/content.expected $testroot/content
ret="$?"
if [ "$ret" != "0" ]; then
return 1
fi
+ if [ ! -h $testroot/wt/dotgotbar.link ]; then
+ echo "dotgotbar.link is not a symlink"
+ test_done "$testroot" "1"
+ return 1
+ fi
+ readlink $testroot/wt/dotgotbar.link > $testroot/stdout
+ echo ".got/bar" > $testroot/stdout.expected
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
if [ -h $testroot/wt/dotgotfoo.link ]; then
echo "dotgotfoo.link is a symlink"
test_done "$testroot" "1"
run_test test_stage_patch_quit
run_test test_stage_patch_incomplete_script
run_test test_stage_symlink
+run_test test_stage_patch_symlink