Commit Diff


commit - b88d214a31981ae2610d930c34d956143b03c523
commit + 2e1fa22206f9dbaca888c89bb9938782cbb4c5a1
blob - f15a1b26fc74d03f67098f4b87ea6be4be5328d6
blob + 13820ca28ed7e43a9699728547d5b98015959174
--- lib/fileindex.c
+++ lib/fileindex.c
@@ -178,6 +178,29 @@ got_fileindex_entry_stage_set(struct got_fileindex_ent
 }
 
 int
+got_fileindex_entry_filetype_get(struct got_fileindex_entry *ie)
+{
+	return (ie->mode & GOT_FILEIDX_MODE_FILE_TYPE);
+}
+
+const struct got_error *
+got_fileindex_entry_filetype_set(struct got_fileindex_entry *ie, int type)
+{
+	switch (type) {
+	case GOT_FILEIDX_MODE_REGULAR_FILE:
+	case GOT_FILEIDX_MODE_SYMLINK:
+	case GOT_FILEIDX_MODE_BAD_SYMLINK:
+		break;
+	default:
+		return got_error(GOT_ERR_BAD_FILETYPE);
+	}
+
+	ie->mode &= ~GOT_FILEIDX_MODE_FILE_TYPE;
+	ie->mode |= type;
+	return NULL;
+}
+
+int
 got_fileindex_entry_has_blob(struct got_fileindex_entry *ie)
 {
 	return (ie->flags & GOT_FILEIDX_F_NO_BLOB) == 0;
blob - af6efe8eca81db02b8067b47cf97720194c03e52
blob + 31f1e0475396f6c86cdec1586d990c4ac6a7f374
--- lib/got_lib_fileindex.h
+++ lib/got_lib_fileindex.h
@@ -39,6 +39,7 @@ struct got_fileindex_entry {
 #define GOT_FILEIDX_MODE_FILE_TYPE	0x000f
 #define GOT_FILEIDX_MODE_REGULAR_FILE	1
 #define GOT_FILEIDX_MODE_SYMLINK	2
+#define GOT_FILEIDX_MODE_BAD_SYMLINK	3
 #define GOT_FILEIDX_MODE_PERMS		0xfff0
 #define GOT_FILEIDX_MODE_PERMS_SHIFT	4
 
@@ -161,5 +162,8 @@ int got_fileindex_entry_has_commit(struct got_fileinde
 int got_fileindex_entry_has_file_on_disk(struct got_fileindex_entry *);
 uint32_t got_fileindex_entry_stage_get(const struct got_fileindex_entry *);
 void got_fileindex_entry_stage_set(struct got_fileindex_entry *ie, uint32_t);
+int got_fileindex_entry_filetype_get(struct got_fileindex_entry *);
+const struct got_error *got_fileindex_entry_filetype_set(
+    struct got_fileindex_entry *, int);
 
 void got_fileindex_entry_mark_deleted_from_disk(struct got_fileindex_entry *);
blob - 7bb6476174e9f1c13193dab9b4a12f2fecb8d3a7
blob + 5abfa0d3ed7655b5c44750c9987fe51452d18035
--- lib/worktree.c
+++ lib/worktree.c
@@ -1197,10 +1197,10 @@ replace_existing_symlink(const char *ondisk_path, cons
 }
 
 static const struct got_error *
-install_symlink(struct got_worktree *worktree, const char *ondisk_path,
-    const char *path, mode_t te_mode, mode_t st_mode,
-    struct got_blob_object *blob, int restoring_missing_file,
-    int reverting_versioned_file, struct got_repository *repo,
+install_symlink(int *is_bad_symlink, struct got_worktree *worktree,
+    const char *ondisk_path, const char *path, struct got_blob_object *blob,
+    int restoring_missing_file, int reverting_versioned_file,
+    struct got_repository *repo,
     got_worktree_checkout_cb progress_cb, void *progress_arg)
 {
 	const struct got_error *err = NULL;
@@ -1211,6 +1211,8 @@ install_symlink(struct got_worktree *worktree, const c
 	const uint8_t *buf = got_object_blob_get_read_buf(blob);
 	size_t hdrlen = got_object_blob_get_hdrlen(blob);
 
+	*is_bad_symlink = 0;
+
 	/* 
 	 * Blob object content specifies the target path of the link.
 	 * If a symbolic link cannot be installed we instead create
@@ -1221,6 +1223,7 @@ install_symlink(struct got_worktree *worktree, const c
 		err = got_object_blob_read_block(&len, blob);
 		if (len + target_len >= sizeof(target_path)) {
 			/* Path too long; install as a regular file. */
+			*is_bad_symlink = 1;
 			got_object_blob_rewind(blob);
 			return install_blob(worktree, ondisk_path, path,
 			    GOT_DEFAULT_FILE_MODE, GOT_DEFAULT_FILE_MODE, blob,
@@ -1271,6 +1274,7 @@ install_symlink(struct got_worktree *worktree, const c
 	    abspath : target_path), worktree->root_path,
 	    strlen(worktree->root_path))) {
 		/* 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,
@@ -1288,6 +1292,7 @@ install_symlink(struct got_worktree *worktree, const c
 	if (got_path_is_child(resolved_path ? resolved_path : (abspath ?
 	    abspath : target_path), path_got, strlen(path_got))) {
 		/* 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,
@@ -1332,6 +1337,7 @@ install_symlink(struct got_worktree *worktree, const c
 		/* Handle errors from first or second creation attempt. */
 		if (errno == ENAMETOOLONG) {
 			/* bad target path; 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,
@@ -1367,11 +1373,6 @@ install_blob(struct got_worktree *worktree, const char
 	int update = 0;
 	char *tmppath = NULL;
 
-	if (S_ISLNK(te_mode))
-		return install_symlink(worktree, ondisk_path, path, te_mode,
-		    st_mode, blob, restoring_missing_file,
-		    reverting_versioned_file, repo, progress_cb, progress_arg);
-
 	fd = open(ondisk_path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW,
 	    GOT_DEFAULT_FILE_MODE);
 	if (fd == -1) {
@@ -1883,9 +1884,18 @@ update_blob(struct got_worktree *worktree,
 		if (err)
 			goto done;
 	} else {
-		err = install_blob(worktree, ondisk_path, path, te->mode,
-		    sb.st_mode, blob, status == GOT_STATUS_MISSING, 0, 0,
-		    repo, progress_cb, progress_arg);
+		int is_bad_symlink = 0;
+		if (S_ISLNK(te->mode)) {
+			err = install_symlink(&is_bad_symlink, worktree,
+			    ondisk_path, path, blob,
+			    status == GOT_STATUS_MISSING, 0, repo,
+			    progress_cb, progress_arg);
+		} else {
+			err = install_blob(worktree, ondisk_path, path,
+			    te->mode, sb.st_mode, blob,
+			    status == GOT_STATUS_MISSING, 0, 0, repo,
+			    progress_cb, progress_arg);
+		}
 		if (err)
 			goto done;
 		if (ie) {
@@ -1898,6 +1908,15 @@ update_blob(struct got_worktree *worktree,
 		}
 		if (err)
 			goto done;
+		if (is_bad_symlink) {
+			if (ie == NULL)
+				ie = got_fileindex_entry_get(fileindex, path,
+				    strlen(path));
+			err = got_fileindex_entry_filetype_set(ie,
+			    GOT_FILEIDX_MODE_BAD_SYMLINK);
+			if (err)
+				goto done;
+		}
 	}
 	got_object_blob_close(blob);
 done:
@@ -2742,10 +2761,17 @@ merge_file_cb(void *arg, struct got_blob_object *blob1
 					goto done;
 			}
 		} else {
+			int is_bad_symlink = 0;
 			sb.st_mode = GOT_DEFAULT_FILE_MODE;
-			err = install_blob(a->worktree, ondisk_path, path2,
-			    mode2, sb.st_mode, blob2, 0, 0, 0, repo,
-			    a->progress_cb, a->progress_arg);
+			if (S_ISLNK(mode2)) {
+				err = install_symlink(&is_bad_symlink,
+				    a->worktree, ondisk_path, path2, blob2, 0,
+				    0, repo, a->progress_cb, a->progress_arg);
+			} else {
+				err = install_blob(a->worktree, ondisk_path, path2,
+				    mode2, sb.st_mode, blob2, 0, 0, 0, repo,
+				    a->progress_cb, a->progress_arg);
+			}
 			if (err)
 				goto done;
 			err = got_fileindex_entry_alloc(&ie, path2);
@@ -2762,6 +2788,12 @@ merge_file_cb(void *arg, struct got_blob_object *blob1
 				got_fileindex_entry_free(ie);
 				goto done;
 			}
+			if (is_bad_symlink) {
+				err = got_fileindex_entry_filetype_set(ie,
+				    GOT_FILEIDX_MODE_BAD_SYMLINK);
+				if (err)
+					goto done;
+			}
 		}
 	}
 done:
@@ -4256,10 +4288,19 @@ revert_file(void *arg, unsigned char status, unsigned 
 				goto done;
 			}
 		} else {
-			err = install_blob(a->worktree, ondisk_path, ie->path,
-			    te ? te->mode : GOT_DEFAULT_FILE_MODE,
-			    got_fileindex_perms_to_st(ie), blob, 0, 1, 0,
-			    a->repo, a->progress_cb, a->progress_arg);
+			int is_bad_symlink = 0;
+			if (te && S_ISLNK(te->mode)) {
+				err = install_symlink(&is_bad_symlink,
+				    a->worktree, ondisk_path, ie->path,
+				    blob, 0, 1, a->repo,
+				    a->progress_cb, a->progress_arg);
+			} else {
+				err = install_blob(a->worktree, ondisk_path,
+				    ie->path,
+				    te ? te->mode : GOT_DEFAULT_FILE_MODE,
+				    got_fileindex_perms_to_st(ie), blob, 0, 1, 0,
+				    a->repo, a->progress_cb, a->progress_arg);
+			}
 			if (err)
 				goto done;
 			if (status == GOT_STATUS_DELETE ||
@@ -4267,6 +4308,12 @@ revert_file(void *arg, unsigned char status, unsigned 
 				err = got_fileindex_entry_update(ie,
 				    ondisk_path, blob->id.sha1,
 				    a->worktree->base_commit_id->sha1, 1);
+				if (err)
+					goto done;
+			}
+			if (is_bad_symlink) {
+				err = got_fileindex_entry_filetype_set(ie,
+				    GOT_FILEIDX_MODE_BAD_SYMLINK);
 				if (err)
 					goto done;
 			}
@@ -4919,6 +4966,106 @@ done:
 }
 
 static const struct got_error *
+reinstall_symlink_after_commit(int *is_bad_symlink, struct got_commitable *ct,
+    struct got_object_id *new_base_commit_id, struct got_worktree *worktree,
+    struct got_repository *repo)
+{
+	const struct got_error *err = NULL;
+	struct got_blob_object *blob = NULL;
+	struct got_object_id *tree_id = NULL;
+	char *tree_path = NULL;
+	struct got_tree_object *tree = NULL;
+	struct got_tree_entry *te;
+	char *entry_name;
+
+	err = got_object_open_as_blob(&blob, repo, ct->blob_id, PATH_MAX);
+	if (err)
+		return err;
+
+	err = got_path_dirname(&tree_path, ct->in_repo_path);
+	if (err) {
+		if (err->code != GOT_ERR_BAD_PATH)
+			goto done;
+		err = got_object_id_by_path(&tree_id, repo,
+		    new_base_commit_id, "");
+		if (err)
+			goto done;
+	} else {
+		err = got_object_id_by_path(&tree_id, repo,
+		    new_base_commit_id, tree_path);
+		if (err)
+			goto done;
+	}
+
+	err = got_object_open_as_tree(&tree, repo, tree_id);
+	if (err)
+		goto done;
+
+	entry_name = basename(ct->path);
+	if (entry_name == NULL) {
+		err = got_error_from_errno2("basename", ct->path);
+		goto done;
+	}
+
+	te = got_object_tree_find_entry(tree, entry_name);
+	if (te == NULL) {
+		err = got_error_path(ct->path, GOT_ERR_NO_TREE_ENTRY);
+		goto done;
+	}
+
+	err = install_symlink(is_bad_symlink, worktree, ct->ondisk_path,
+	    ct->path, blob, 0, 0, repo, NULL, NULL);
+done:
+	if (blob)
+		got_object_blob_close(blob);
+	if (tree)
+		got_object_tree_close(tree);
+	free(tree_id);
+	free(tree_path);
+	return err;
+}
+
+/*
+ * After comitting a symlink we have a chance to convert "bad" symlinks
+ * (those which point outside the work tree or into .got) to regular files.
+ * This way, the post-commit work tree state matches a fresh checkout of
+ * the tree which was just committed. We also mark such newly committed
+ * symlinks as "bad" in the work tree's fileindex.
+ */
+static const struct got_error *
+reinstall_symlinks_after_commit(struct got_pathlist_head *commitable_paths,
+    struct got_object_id *new_base_commit_id, struct got_fileindex *fileindex,
+    struct got_worktree *worktree, struct got_repository *repo)
+{
+	const struct got_error *err = NULL;
+	struct got_pathlist_entry *pe;
+
+	TAILQ_FOREACH(pe, commitable_paths, entry) {
+		struct got_commitable *ct = pe->data;
+		struct got_fileindex_entry *ie;
+		int is_bad_symlink = 0;
+	
+		if (!S_ISLNK(get_ct_file_mode(ct)))
+			continue;
+
+		err = reinstall_symlink_after_commit(&is_bad_symlink,
+		    ct, new_base_commit_id, worktree, repo);
+		if (err)
+			break;
+		ie = got_fileindex_entry_get(fileindex, ct->path,
+		    strlen(ct->path));
+		if (ie && is_bad_symlink) {
+			err = got_fileindex_entry_filetype_set(ie,
+			    GOT_FILEIDX_MODE_BAD_SYMLINK);
+			if (err)
+				break;
+		}
+	}
+
+	return err;
+}
+
+static const struct got_error *
 update_fileindex_after_commit(struct got_pathlist_head *commitable_paths,
     struct got_object_id *new_base_commit_id, struct got_fileindex *fileindex,
     int have_staged_files)
@@ -5074,35 +5221,9 @@ commit_worktree(struct got_object_id **new_commit_id,
 			goto done;
 		}
 		err = got_object_blob_create(&ct->blob_id, ondisk_path, repo);
-		if (err) {
-			free(ondisk_path);
+		free(ondisk_path);
+		if (err)
 			goto done;
-		}
-
-		/*
-		 * When comitting a symlink we convert "bad" symlinks (those
-		 * which point outside the work tree or into .got) to regular
-		 * files. This way, the post-commit work tree state matches
-		 * a fresh checkout of the tree which was committed.
-		 */
-		if (S_ISLNK(get_ct_file_mode(ct))) {
-			struct got_blob_object *blob;
-			err = got_object_open_as_blob(&blob, repo, ct->blob_id,
-			    PATH_MAX); 
-			if (err) {
-				free(ondisk_path);
-				goto done;
-			}
-			err = install_symlink(worktree, ondisk_path, ct->path,
-			    get_ct_file_mode(ct), GOT_DEFAULT_FILE_MODE, blob,
-			    0, 0, repo, NULL, NULL);
-			got_object_blob_close(blob);
-			if (err) {
-				free(ondisk_path);
-				goto done;
-			}
-		}
-		free(ondisk_path);
 	}
 
 	/* Recursively write new tree objects. */
@@ -5321,6 +5442,10 @@ got_worktree_commit(struct got_object_id **new_commit_
 
 	err = update_fileindex_after_commit(&commitable_paths, *new_commit_id,
 	    fileindex, have_staged_files);
+	if (err == NULL) {
+		err = reinstall_symlinks_after_commit(&commitable_paths,
+		    *new_commit_id, fileindex, worktree, repo);
+	}
 	sync_err = sync_fileindex(fileindex, fileindex_path);
 	if (sync_err && err == NULL)
 		err = sync_err;