Commit Diff


commit - f3b2b5525998f769dba67674168f3ffb3cc495e5
commit + 437adc9d5be73b4b89441362ec89de754374a5a6
blob - 04443aa094268bbea1d505ec47ffe7bac202ab71
blob + 5796af500a49c702c5c1d95f32551f881ad0f9b5
--- lib/fileindex.c
+++ lib/fileindex.c
@@ -86,15 +86,15 @@ got_fileindex_perms_to_st(struct got_fileindex_entry *
 
 const struct got_error *
 got_fileindex_entry_update(struct got_fileindex_entry *ie,
-    const char *ondisk_path, uint8_t *blob_sha1, uint8_t *commit_sha1,
-    int update_timestamps)
+    int wt_fd, const char *ondisk_path, uint8_t *blob_sha1,
+    uint8_t *commit_sha1, int update_timestamps)
 {
 	struct stat sb;
 
-	if (lstat(ondisk_path, &sb) != 0) {
+	if (fstatat(wt_fd, ondisk_path, &sb, AT_SYMLINK_NOFOLLOW) != 0) {
 		if (!((ie->flags & GOT_FILEIDX_F_NO_FILE_ON_DISK) &&
 		    errno == ENOENT))
-			return got_error_from_errno2("lstat", ondisk_path);
+			return got_error_from_errno2("fstatat", ondisk_path);
 		sb.st_mode = GOT_DEFAULT_FILE_MODE;
 	} else {
 		if (sb.st_mode & S_IFDIR)
blob - f10058320f24affa0531641620b65ac3641a9b6f
blob + 537374d719e84293f1b449465ab63e092b44a20a
--- lib/got_lib_fileindex.h
+++ lib/got_lib_fileindex.h
@@ -108,7 +108,7 @@ uint16_t got_fileindex_perms_from_st(struct stat *);
 mode_t got_fileindex_perms_to_st(struct got_fileindex_entry *);
 
 const struct got_error *got_fileindex_entry_update(struct got_fileindex_entry *,
-    const char *, uint8_t *, uint8_t *, int);
+    int, const char *, uint8_t *, uint8_t *, int);
 const struct got_error *got_fileindex_entry_alloc(struct got_fileindex_entry **,
     const char *);
 void got_fileindex_entry_free(struct got_fileindex_entry *);
blob - 067bac22c55a94684a7dcb779417671747de7972
blob + 651bcc8f757194fa0d5259093255234ac99ee09d
--- lib/got_lib_worktree.h
+++ lib/got_lib_worktree.h
@@ -17,6 +17,7 @@
 struct got_worktree {
 	char *root_path;
 	char *repo_path;
+	int root_fd;
 	char *path_prefix;
 	struct got_object_id *base_commit_id;
 	char *head_ref_name;
blob - 971955fbafa7d712d0e5a61dfe60def65321b0f0
blob + 91a87ba60ac2ebc61140d6778df16fc1e2f39016
--- lib/worktree.c
+++ lib/worktree.c
@@ -429,6 +429,12 @@ open_worktree(struct got_worktree **worktree, const ch
 
 	err = got_gotconfig_read(&(*worktree)->gotconfig,
 	    (*worktree)->gotconfig_path);
+
+	(*worktree)->root_fd = open((*worktree)->root_path, O_DIRECTORY);
+	if ((*worktree)->root_fd == -1) {
+		err = got_error_from_errno2("open", (*worktree)->root_path);
+		goto done;
+	}
 done:
 	if (repo)
 		got_repo_close(repo);
@@ -505,6 +511,7 @@ got_worktree_close(struct got_worktree *worktree)
 	free(worktree->gotconfig_path);
 	got_gotconfig_free(worktree->gotconfig);
 	free(worktree);
+	close(worktree->root_fd);
 	return err;
 }
 
@@ -1177,7 +1184,7 @@ done:
 static const struct got_error *
 create_fileindex_entry(struct got_fileindex_entry **new_iep,
     struct got_fileindex *fileindex, struct got_object_id *base_commit_id,
-    const char *ondisk_path, const char *path, struct got_object_id *blob_id)
+    int wt_fd, const char *path, struct got_object_id *blob_id)
 {
 	const struct got_error *err = NULL;
 	struct got_fileindex_entry *new_ie;
@@ -1188,7 +1195,7 @@ create_fileindex_entry(struct got_fileindex_entry **ne
 	if (err)
 		return err;
 
-	err = got_fileindex_entry_update(new_ie, ondisk_path,
+	err = got_fileindex_entry_update(new_ie, wt_fd, path,
 	    blob_id->sha1, base_commit_id->sha1, 1);
 	if (err)
 		goto done;
@@ -1863,11 +1870,11 @@ done:
  * we had to run a full content comparison to find out.
  */
 static const struct got_error *
-sync_timestamps(char *ondisk_path, unsigned char status,
+sync_timestamps(int wt_fd, const char *path, unsigned char status,
     struct got_fileindex_entry *ie, struct stat *sb)
 {
 	if (status == GOT_STATUS_NO_CHANGE && stat_info_differs(ie, sb))
-		return got_fileindex_entry_update(ie, ondisk_path,
+		return got_fileindex_entry_update(ie, wt_fd, path,
 		    ie->blob_sha1, ie->commit_sha1, 1);
 
 	return NULL;
@@ -1920,7 +1927,8 @@ update_blob(struct got_worktree *worktree,
 		if (got_fileindex_entry_has_commit(ie) &&
 		    memcmp(ie->commit_sha1, worktree->base_commit_id->sha1,
 		    SHA1_DIGEST_LENGTH) == 0) {
-			err = sync_timestamps(ondisk_path, status, ie, &sb);
+			err = sync_timestamps(worktree->root_fd,
+			    path, status, ie, &sb);
 			if (err)
 				goto done;
 			err = (*progress_cb)(progress_arg, GOT_STATUS_EXISTS,
@@ -1930,7 +1938,8 @@ update_blob(struct got_worktree *worktree,
 		if (got_fileindex_entry_has_blob(ie) &&
 		    memcmp(ie->blob_sha1, te->id.sha1,
 		    SHA1_DIGEST_LENGTH) == 0) {
-			err = sync_timestamps(ondisk_path, status, ie, &sb);
+			err = sync_timestamps(worktree->root_fd,
+			    path, status, ie, &sb);
 			goto done;
 		}
 	}
@@ -1989,17 +1998,17 @@ update_blob(struct got_worktree *worktree,
 		 * Otherwise, a future status walk would treat them as
 		 * unmodified files again.
 		 */
-		err = got_fileindex_entry_update(ie, ondisk_path,
+		err = got_fileindex_entry_update(ie, worktree->root_fd, path,
 		    blob->id.sha1, worktree->base_commit_id->sha1,
 		    update_timestamps);
 	} else if (status == GOT_STATUS_MODE_CHANGE) {
-		err = got_fileindex_entry_update(ie, ondisk_path,
+		err = got_fileindex_entry_update(ie, worktree->root_fd, path,
 		    blob->id.sha1, worktree->base_commit_id->sha1, 0);
 	} else if (status == GOT_STATUS_DELETE) {
 		err = (*progress_cb)(progress_arg, GOT_STATUS_MERGE, path);
 		if (err)
 			goto done;
-		err = got_fileindex_entry_update(ie, ondisk_path,
+		err = got_fileindex_entry_update(ie, worktree->root_fd, path,
 		    blob->id.sha1, worktree->base_commit_id->sha1, 0);
 		if (err)
 			goto done;
@@ -2022,11 +2031,12 @@ update_blob(struct got_worktree *worktree,
 			goto done;
 
 		if (ie) {
-			err = got_fileindex_entry_update(ie, ondisk_path,
-			    blob->id.sha1, worktree->base_commit_id->sha1, 1);
+			err = got_fileindex_entry_update(ie,
+			    worktree->root_fd, path, blob->id.sha1,
+			    worktree->base_commit_id->sha1, 1);
 		} else {
 			err = create_fileindex_entry(&ie, fileindex,
-			    worktree->base_commit_id, ondisk_path, path,
+			    worktree->base_commit_id, worktree->root_fd, path,
 			    &blob->id);
 		}
 		if (err)
@@ -2126,8 +2136,8 @@ delete_blob(struct got_worktree *worktree, struct got_
 		 * Preserve the working file and change the deleted blob's
 		 * entry into a schedule-add entry.
 		 */
-		err = got_fileindex_entry_update(ie, ondisk_path, NULL, NULL,
-		    0);
+		err = got_fileindex_entry_update(ie, worktree->root_fd,
+		    ie->path, NULL, NULL, 0);
 	} else {
 		err = (*progress_cb)(progress_arg, GOT_STATUS_DELETE, ie->path);
 		if (err)
@@ -2935,7 +2945,7 @@ merge_file_cb(void *arg, struct got_blob_object *blob1
 				goto done;
 			if (status == GOT_STATUS_DELETE) {
 				err = got_fileindex_entry_update(ie,
-				    ondisk_path, blob2->id.sha1,
+				    a->worktree->root_fd, path2, blob2->id.sha1,
 				    a->worktree->base_commit_id->sha1, 0);
 				if (err)
 					goto done;
@@ -2957,8 +2967,8 @@ merge_file_cb(void *arg, struct got_blob_object *blob1
 			err = got_fileindex_entry_alloc(&ie, path2);
 			if (err)
 				goto done;
-			err = got_fileindex_entry_update(ie, ondisk_path,
-			    NULL, NULL, 1);
+			err = got_fileindex_entry_update(ie,
+			    a->worktree->root_fd, path2, NULL, NULL, 1);
 			if (err) {
 				got_fileindex_entry_free(ie);
 				goto done;
@@ -3777,7 +3787,8 @@ schedule_addition(void *arg, unsigned char status, uns
 	err = got_fileindex_entry_alloc(&ie, relpath);
 	if (err)
 		goto done;
-	err = got_fileindex_entry_update(ie, ondisk_path, NULL, NULL, 1);
+	err = got_fileindex_entry_update(ie, a->worktree->root_fd,
+	    relpath, NULL, NULL, 1);
 	if (err) {
 		got_fileindex_entry_free(ie);
 		goto done;
@@ -4584,7 +4595,8 @@ revert_file(void *arg, unsigned char status, unsigned 
 			if (status == GOT_STATUS_DELETE ||
 			    status == GOT_STATUS_MODE_CHANGE) {
 				err = got_fileindex_entry_update(ie,
-				    ondisk_path, blob->id.sha1,
+				    a->worktree->root_fd, relpath,
+				    blob->id.sha1,
 				    a->worktree->base_commit_id->sha1, 1);
 				if (err)
 					goto done;
@@ -5287,18 +5299,26 @@ done:
 }
 
 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)
+update_fileindex_after_commit(struct got_worktree *worktree,
+    struct got_pathlist_head *commitable_paths,
+    struct got_object_id *new_base_commit_id,
+    struct got_fileindex *fileindex, int have_staged_files)
 {
 	const struct got_error *err = NULL;
 	struct got_pathlist_entry *pe;
+	char *relpath = NULL;
 
 	TAILQ_FOREACH(pe, commitable_paths, entry) {
 		struct got_fileindex_entry *ie;
 		struct got_commitable *ct = pe->data;
 
 		ie = got_fileindex_entry_get(fileindex, pe->path, pe->path_len);
+
+		err = got_path_skip_common_ancestor(&relpath,
+		    worktree->root_path, ct->ondisk_path);
+		if (err)
+			goto done;
+
 		if (ie) {
 			if (ct->status == GOT_STATUS_DELETE ||
 			    ct->staged_status == GOT_STATUS_DELETE) {
@@ -5308,32 +5328,40 @@ update_fileindex_after_commit(struct got_pathlist_head
 				got_fileindex_entry_stage_set(ie,
 				    GOT_FILEIDX_STAGE_NONE);
 				got_fileindex_entry_staged_filetype_set(ie, 0);
+
 				err = got_fileindex_entry_update(ie,
-				    ct->ondisk_path, ct->staged_blob_id->sha1,
+				    worktree->root_fd, relpath,
+				    ct->staged_blob_id->sha1,
 				    new_base_commit_id->sha1,
 				    !have_staged_files);
 			} else
 				err = got_fileindex_entry_update(ie,
-				    ct->ondisk_path, ct->blob_id->sha1,
+				    worktree->root_fd, relpath,
+				    ct->blob_id->sha1,
 				    new_base_commit_id->sha1,
 				    !have_staged_files);
 		} else {
 			err = got_fileindex_entry_alloc(&ie, pe->path);
 			if (err)
-				break;
-			err = got_fileindex_entry_update(ie, ct->ondisk_path,
-			    ct->blob_id->sha1, new_base_commit_id->sha1, 1);
+				goto done;
+			err = got_fileindex_entry_update(ie,
+			    worktree->root_fd, relpath, ct->blob_id->sha1,
+			    new_base_commit_id->sha1, 1);
 			if (err) {
 				got_fileindex_entry_free(ie);
-				break;
+				goto done;
 			}
 			err = got_fileindex_entry_add(fileindex, ie);
 			if (err) {
 				got_fileindex_entry_free(ie);
-				break;
+				goto done;
 			}
 		}
+		free(relpath);
+		relpath = NULL;
 	}
+done:
+	free(relpath);
 	return err;
 }
 
@@ -5664,8 +5692,8 @@ got_worktree_commit(struct got_object_id **new_commit_
 	if (err)
 		goto done;
 
-	err = update_fileindex_after_commit(&commitable_paths, *new_commit_id,
-	    fileindex, have_staged_files);
+	err = update_fileindex_after_commit(worktree, &commitable_paths,
+	    *new_commit_id, fileindex, have_staged_files);
 	sync_err = sync_fileindex(fileindex, fileindex_path);
 	if (sync_err && err == NULL)
 		err = sync_err;
@@ -6250,8 +6278,8 @@ rebase_commit(struct got_object_id **new_commit_id,
 	if (err)
 		goto done;
 
-	err = update_fileindex_after_commit(&commitable_paths, *new_commit_id,
-	    fileindex, 0);
+	err = update_fileindex_after_commit(worktree, &commitable_paths,
+	    *new_commit_id, fileindex, 0);
 	sync_err = sync_fileindex(fileindex, fileindex_path);
 	if (sync_err && err == NULL)
 		err = sync_err;