do not update symlinks which are already up-to-date This fixes spurious 'U' notifications for symlinks during 'got update' that occurred even when the work tree was fully up-to-date. Observed on a work tree of the FreeBSD src repo and reproduced in our test suite by adding a no-op update at the end of a test which deals with updating symlinks.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
diff --git a/lib/worktree.c b/lib/worktree.c
index d3e4837..d201278 100644
--- a/lib/worktree.c
+++ b/lib/worktree.c
@@ -1244,14 +1244,16 @@ install_blob(struct got_worktree *worktree, const char *ondisk_path,
* safe location in the work tree!
*/
static const struct got_error *
-replace_existing_symlink(const char *ondisk_path, const char *target_path,
- size_t target_len)
+replace_existing_symlink(int *did_something, const char *ondisk_path,
+ const char *target_path, size_t target_len)
{
const struct got_error *err = NULL;
ssize_t elen;
char etarget[PATH_MAX];
int fd;
+ *did_something = 0;
+
/*
* "Bad" symlinks (those pointing outside the work tree or into the
* .got directory) are installed in the work tree as a regular file
@@ -1275,6 +1277,7 @@ replace_existing_symlink(const char *ondisk_path, const char *target_path,
return NULL; /* nothing to do */
}
+ *did_something = 1;
err = update_symlink(ondisk_path, target_path, target_len);
if (fd != -1 && close(fd) == -1 && err == NULL)
err = got_error_from_errno2("close", ondisk_path);
@@ -1396,7 +1399,6 @@ install_symlink(int *is_bad_symlink, struct got_worktree *worktree,
if (*is_bad_symlink) {
/* install as a regular file */
- *is_bad_symlink = 1;
got_object_blob_rewind(blob);
err = install_blob(worktree, ondisk_path, path,
GOT_DEFAULT_FILE_MODE, GOT_DEFAULT_FILE_MODE, blob,
@@ -1407,20 +1409,26 @@ install_symlink(int *is_bad_symlink, struct got_worktree *worktree,
if (symlink(target_path, ondisk_path) == -1) {
if (errno == EEXIST) {
+ int symlink_replaced;
if (path_is_unversioned) {
err = (*progress_cb)(progress_arg,
GOT_STATUS_UNVERSIONED, path);
goto done;
}
- err = replace_existing_symlink(ondisk_path,
- target_path, target_len);
+ err = replace_existing_symlink(&symlink_replaced,
+ ondisk_path, target_path, target_len);
if (err)
goto done;
if (progress_cb) {
- err = (*progress_cb)(progress_arg,
- reverting_versioned_file ?
- GOT_STATUS_REVERT : GOT_STATUS_UPDATE,
- path);
+ if (symlink_replaced) {
+ err = (*progress_cb)(progress_arg,
+ reverting_versioned_file ?
+ GOT_STATUS_REVERT :
+ GOT_STATUS_UPDATE, path);
+ } else {
+ err = (*progress_cb)(progress_arg,
+ GOT_STATUS_EXISTS, path);
+ }
}
goto done; /* Nothing else to do. */
}
@@ -1928,11 +1936,19 @@ update_blob(struct got_worktree *worktree,
goto done;
}
- if (ie && status != GOT_STATUS_MISSING &&
- (te->mode & S_IXUSR) == (sb.st_mode & S_IXUSR)) {
+ if (ie && status != GOT_STATUS_MISSING && S_ISREG(sb.st_mode) &&
+ (S_ISLNK(te->mode) ||
+ (te->mode & S_IXUSR) == (sb.st_mode & S_IXUSR))) {
+ /*
+ * This is a regular file or an installed bad symlink.
+ * If the file index indicates that this file is already
+ * up-to-date with respect to the repository we can skip
+ * updating contents of this file.
+ */
if (got_fileindex_entry_has_commit(ie) &&
memcmp(ie->commit_sha1, worktree->base_commit_id->sha1,
SHA1_DIGEST_LENGTH) == 0) {
+ /* Same commit. */
err = sync_timestamps(worktree->root_fd,
path, status, ie, &sb);
if (err)
@@ -1944,6 +1960,7 @@ update_blob(struct got_worktree *worktree,
if (got_fileindex_entry_has_blob(ie) &&
memcmp(ie->blob_sha1, te->id.sha1,
SHA1_DIGEST_LENGTH) == 0) {
+ /* Different commit but the same blob. */
err = sync_timestamps(worktree->root_fd,
path, status, ie, &sb);
if (err)
diff --git a/regress/cmdline/update.sh b/regress/cmdline/update.sh
index 2623bfe..c90dd13 100755
--- a/regress/cmdline/update.sh
+++ b/regress/cmdline/update.sh
@@ -1929,6 +1929,17 @@ test_update_adds_symlink() {
ret="$?"
if [ "$ret" != "0" ]; then
diff -u $testroot/stdout.expected $testroot/stdout
+ test_done "$testroot" "$ret"
+ return 1
+ fi
+
+ # Updating an up-to-date symlink should be a no-op.
+ echo 'Already up-to-date' > $testroot/stdout.expected
+ (cd $testroot/wt && got update > $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"
}