commit 3d9a4ec407702ad2b932c522001f1b88a36571de from: Stefan Sperling date: Thu Jul 23 14:21:30 2020 UTC add symlink support to 'got commit' commit - af57b12ab516c7fa5ecc8bd00db5637240411ed7 commit + 3d9a4ec407702ad2b932c522001f1b88a36571de blob - fa3eec221ff24961d74441b2e25a3a6186877ecd blob + cf8a72840c0b31087b00b5d1bfe09ed1eb4479ad --- lib/object_create.c +++ lib/object_create.c @@ -128,10 +128,15 @@ got_object_blob_create(struct got_object_id **id, cons SHA1Init(&sha1_ctx); fd = open(ondisk_path, O_RDONLY | O_NOFOLLOW); - if (fd == -1) - return got_error_from_errno2("open", ondisk_path); + if (fd == -1) { + if (errno != ELOOP) + return got_error_from_errno2("open", ondisk_path); - if (fstat(fd, &sb) == -1) { + if (lstat(ondisk_path, &sb) == -1) { + err = got_error_from_errno2("lstat", ondisk_path); + goto done; + } + } else if (fstat(fd, &sb) == -1) { err = got_error_from_errno2("fstat", ondisk_path); goto done; } @@ -156,13 +161,21 @@ got_object_blob_create(struct got_object_id **id, cons goto done; } for (;;) { - char buf[8192]; + char buf[PATH_MAX]; ssize_t inlen; - inlen = read(fd, buf, sizeof(buf)); - if (inlen == -1) { - err = got_error_from_errno("read"); - goto done; + if (S_ISLNK(sb.st_mode)) { + inlen = readlink(ondisk_path, buf, sizeof(buf)); + if (inlen == -1) { + err = got_error_from_errno("readlink"); + goto done; + } + } else { + inlen = read(fd, buf, sizeof(buf)); + if (inlen == -1) { + err = got_error_from_errno("read"); + goto done; + } } if (inlen == 0) break; /* EOF */ @@ -172,6 +185,8 @@ got_object_blob_create(struct got_object_id **id, cons err = got_ferror(blobfile, GOT_ERR_IO); goto done; } + if (S_ISLNK(sb.st_mode)) + break; } *id = malloc(sizeof(**id)); @@ -218,6 +233,8 @@ te_mode2str(char *buf, size_t len, struct got_tree_ent mode |= S_IXUSR | S_IXGRP | S_IXOTH; } else if (got_object_tree_entry_is_submodule(te)) mode = S_IFDIR | S_IFLNK; + else if (S_ISLNK(te->mode)) + mode = S_IFLNK; /* Git leaves all the other bits unset. */ else if (S_ISDIR(te->mode)) mode = S_IFDIR; /* Git leaves all the other bits unset. */ else blob - 18b7915d9fcf0ce6358373186138b917bbf2f1ba blob + 4f2a83243d33c26ba5cf655c2b3e82a61f8d5517 --- lib/worktree.c +++ lib/worktree.c @@ -4343,6 +4343,9 @@ match_ct_parent_path(int *match, struct got_commitable static mode_t get_ct_file_mode(struct got_commitable *ct) { + if (S_ISLNK(ct->mode)) + return S_IFLNK; + return S_IFREG | (ct->mode & ((S_IRWXU | S_IRWXG | S_IRWXO))); } blob - 190d9e7cedd9ad2b17f54f971ebcf4cc3acd7c3f blob + 9bf3085d0b6f3078e98d58aaff1f4e4e4525bff9 --- regress/cmdline/commit.sh +++ regress/cmdline/commit.sh @@ -893,12 +893,125 @@ function test_commit_with_unrelated_submodule { local head_rev=`git_show_head $testroot/repo` echo "M alpha" > $testroot/stdout.expected echo "Created commit $head_rev" >> $testroot/stdout.expected + + 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_commit_symlink { + local testroot=`test_init commit_symlink` + + got checkout $testroot/repo $testroot/wt > /dev/null + ret="$?" + if [ "$ret" != "0" ]; then + test_done "$testroot" "$ret" + return 1 + fi + + (cd $testroot/wt && ln -s alpha alpha.link) + (cd $testroot/wt && ln -s epsilon epsilon.link) + (cd $testroot/wt && ln -s /etc/passwd passwd.link) + (cd $testroot/wt && ln -s ../beta epsilon/beta.link) + (cd $testroot/wt && ln -s nonexistent nonexistent.link) + (cd $testroot/wt && got add alpha.link epsilon.link passwd.link \ + epsilon/beta.link nonexistent.link > /dev/null) + + (cd $testroot/wt && got commit -m 'test commit_symlink' > $testroot/stdout) + + local head_rev=`git_show_head $testroot/repo` + echo "A alpha.link" > $testroot/stdout.expected + echo "A epsilon.link" >> $testroot/stdout.expected + echo "A nonexistent.link" >> $testroot/stdout.expected + echo "A passwd.link" >> $testroot/stdout.expected + echo "A epsilon/beta.link" >> $testroot/stdout.expected + echo "Created commit $head_rev" >> $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 checkout $testroot/repo $testroot/wt2 > /dev/null + ret="$?" + if [ "$ret" != "0" ]; then + test_done "$testroot" "$ret" + return 1 + fi + + if ! [ -h $testroot/wt2/alpha.link ]; then + echo "alpha.link is not a symlink" + test_done "$testroot" "1" + return 1 + fi + + readlink $testroot/wt2/alpha.link > $testroot/stdout + echo "alpha" > $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/wt2/epsilon.link ]; then + echo "epsilon.link is not a symlink" + test_done "$testroot" "1" + return 1 + fi + + readlink $testroot/wt2/epsilon.link > $testroot/stdout + echo "epsilon" > $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/wt2/passwd.link ]; then + echo -n "passwd.link symlink points outside of work tree: " >&2 + readlink $testroot/wt2/passwd.link >&2 + test_done "$testroot" "1" + return 1 + fi + + echo -n "/etc/passwd" > $testroot/content.expected + cp $testroot/wt2/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 + + readlink $testroot/wt2/epsilon/beta.link > $testroot/stdout + echo "../beta" > $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 + + readlink $testroot/wt2/nonexistent.link > $testroot/stdout + echo "nonexistent" > $testroot/stdout.expected + cmp -s $testroot/stdout.expected $testroot/stdout + ret="$?" + if [ "$ret" != "0" ]; then + diff -u $testroot/stdout.expected $testroot/stdout + fi test_done "$testroot" "$ret" } @@ -922,3 +1035,4 @@ run_test test_commit_gitconfig_author run_test test_commit_xbit_change run_test test_commit_normalizes_filemodes run_test test_commit_with_unrelated_submodule +run_test test_commit_symlink