commit - 00bb5ea05eb54b4ec01ed195104765f7baf80169
commit + a919d5c4ebaba5cf7b48888745d8b554acf6fe3e
blob - 9014f142cbca2af04a79adbc2efa7223ad338695
blob + d893d9cfadfcf9aec8b015b5ba0dec0d5eaa887e
--- lib/worktree.c
+++ lib/worktree.c
return GOT_STATUS_MODIFY;
default:
return GOT_STATUS_NO_CHANGE;
+ }
+}
+
+static const struct got_error *
+get_symlink_status(unsigned char *status, struct stat *sb,
+ struct got_fileindex_entry *ie, const char *abspath,
+ int dirfd, const char *de_name, struct got_blob_object *blob)
+{
+ const struct got_error *err = NULL;
+ char target_path[PATH_MAX];
+ char etarget[PATH_MAX];
+ ssize_t elen;
+ size_t len, target_len = 0;
+ const uint8_t *buf = got_object_blob_get_read_buf(blob);
+ size_t hdrlen = got_object_blob_get_hdrlen(blob);
+
+ *status = GOT_STATUS_NO_CHANGE;
+
+ /* Blob object content specifies the target path of the link. */
+ do {
+ err = got_object_blob_read_block(&len, blob);
+ if (err)
+ return err;
+ if (len + target_len >= sizeof(target_path)) {
+ /*
+ * Should not happen. The blob contents were OK
+ * when this symlink was installed.
+ */
+ return got_error(GOT_ERR_NO_SPACE);
+ }
+ if (len > 0) {
+ /* Skip blob object header first time around. */
+ memcpy(target_path + target_len, buf + hdrlen,
+ len - hdrlen);
+ target_len += len - hdrlen;
+ hdrlen = 0;
+ }
+ } while (len != 0);
+ target_path[target_len] = '\0';
+
+ if (dirfd != -1) {
+ elen = readlinkat(dirfd, de_name, etarget, sizeof(etarget));
+ if (elen == -1)
+ return got_error_from_errno2("readlinkat", abspath);
+ } else {
+ elen = readlink(abspath, etarget, sizeof(etarget));
+ if (elen == -1)
+ return got_error_from_errno2("readlinkat", abspath);
}
+
+ if (elen != target_len || memcmp(etarget, target_path, target_len) != 0)
+ *status = GOT_STATUS_MODIFY;
+
+ return NULL;
}
static const struct got_error *
}
} else {
fd = open(abspath, O_RDONLY | O_NOFOLLOW);
- if (fd == -1 && errno != ENOENT)
+ if (fd == -1 && errno != ENOENT && errno != ELOOP)
return got_error_from_errno2("open", abspath);
- if (fd == -1 || fstat(fd, sb) == -1) {
+ else if (fd == -1 && errno == ELOOP) {
+ if (lstat(abspath, sb) == -1)
+ return got_error_from_errno2("lstat", abspath);
+ } else if (fd == -1 || fstat(fd, sb) == -1) {
if (errno == ENOENT) {
if (got_fileindex_entry_has_file_on_disk(ie))
*status = GOT_STATUS_MISSING;
if (err)
goto done;
+ if (S_ISLNK(sb->st_mode)) {
+ /* Staging changes to symlinks is not yet(?) supported. */
+ if (staged_status != GOT_STATUS_NO_CHANGE) {
+ err = got_error_path(abspath, GOT_ERR_FILE_STATUS);
+ goto done;
+ }
+ err = get_symlink_status(status, sb, ie, abspath, dirfd,
+ de_name, blob);
+ goto done;
+ }
+
+
if (dirfd != -1) {
fd = openat(dirfd, de_name, O_RDONLY | O_NOFOLLOW);
if (fd == -1) {
blob - d96dfe3602ef3a7ee2a20198083c20e833f9567a
blob + 6a7dff907ff3ed86eb1cd3bffb039353bcb2d29b
--- regress/cmdline/rm.sh
+++ regress/cmdline/rm.sh
echo "D epsilon/foo/bar/baz/f.o" >> $testroot/stdout.expected
(cd $testroot/wt && got status > $testroot/stdout)
+
+ cmp -s $testroot/stdout.expected $testroot/stdout
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ diff -u $testroot/stdout.expected $testroot/stdout
+ fi
+ test_done "$testroot" "$ret"
+}
+
+function test_rm_symlink {
+ local testroot=`test_init rm_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 && git add .)
+ git_commit $testroot/repo -m "add a symlink"
+
+ got checkout $testroot/repo $testroot/wt > /dev/null
+ ret="$?"
+ if [ "$ret" != "0" ]; then
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+ echo 'D alpha.link' > $testroot/stdout.expected
+ echo 'D epsilon.link' >> $testroot/stdout.expected
+ echo 'D passwd.link' >> $testroot/stdout.expected
+ (cd $testroot/wt && got rm alpha.link epsilon.link passwd.link > \
+ $testroot/stdout)
+
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" "$ret"
+
}
run_test test_rm_basic
run_test test_rm_directory
run_test test_rm_directory_keep_files
run_test test_rm_subtree
+run_test test_rm_symlink