Commit Diff


commit - c631b1152565e4c18cb2123e1d3d7c08d513c02f
commit + 0aeb8099a04ea427eff4a7b6cb52b1cba62a87b0
blob - 3132285fde2f86c533ea3117e76a6f577c8fcb05
blob + 55184665f7b445e704a80de758c82371bc3a475b
--- lib/fileindex.c
+++ lib/fileindex.c
@@ -53,8 +53,8 @@ struct got_fileindex {
 #define GOT_FILEIDX_MAX_ENTRIES INT_MAX
 };
 
-static mode_t
-fileindex_entry_perms_get(struct got_fileindex_entry *ie)
+mode_t
+got_fileindex_entry_perms_get(struct got_fileindex_entry *ie)
 {
 	return ((ie->mode & GOT_FILEIDX_MODE_PERMS) >>
 	    GOT_FILEIDX_MODE_PERMS_SHIFT);
@@ -71,7 +71,7 @@ fileindex_entry_perms_set(struct got_fileindex_entry *
 mode_t
 got_fileindex_perms_to_st(struct got_fileindex_entry *ie)
 {
-	mode_t perms = fileindex_entry_perms_get(ie);
+	mode_t perms = got_fileindex_entry_perms_get(ie);
 	int type = got_fileindex_entry_filetype_get(ie);
 	uint32_t ftype;
 
blob - 8535201970c1361dcc48c9404c68333b0a77b2c5
blob + f10058320f24affa0531641620b65ac3641a9b6f
--- lib/got_lib_fileindex.h
+++ lib/got_lib_fileindex.h
@@ -103,6 +103,7 @@ struct got_fileindex_hdr {
 	uint8_t sha1[SHA1_DIGEST_LENGTH]; /* checksum of above on-disk data */
 };
 
+mode_t got_fileindex_entry_perms_get(struct got_fileindex_entry *);
 uint16_t got_fileindex_perms_from_st(struct stat *);
 mode_t got_fileindex_perms_to_st(struct got_fileindex_entry *);
 
blob - bde4162e571552b42ca8aea844c0fb7d81ee59e3
blob + a24230cc8594d13472a3bcf7b8a5fb461cab1138
--- lib/worktree.c
+++ lib/worktree.c
@@ -4459,6 +4459,7 @@ struct collect_commitables_arg {
 	struct got_pathlist_head *commitable_paths;
 	struct got_repository *repo;
 	struct got_worktree *worktree;
+	struct got_fileindex *fileindex;
 	int have_staged_files;
 };
 
@@ -4516,9 +4517,27 @@ collect_commitables(void *arg, unsigned char status,
 		err = got_error_from_errno("asprintf");
 		goto done;
 	}
-	if (status == GOT_STATUS_DELETE || staged_status == GOT_STATUS_DELETE) {
-		sb.st_mode = GOT_DEFAULT_FILE_MODE;
-	} else {
+
+	if (staged_status == GOT_STATUS_ADD ||
+	    staged_status == GOT_STATUS_MODIFY) {
+		struct got_fileindex_entry *ie;
+		ie = got_fileindex_entry_get(a->fileindex, path, strlen(path));
+		switch (got_fileindex_entry_staged_filetype_get(ie)) {
+		case GOT_FILEIDX_MODE_REGULAR_FILE:
+		case GOT_FILEIDX_MODE_BAD_SYMLINK:
+			ct->mode = S_IFREG;
+			break;
+		case GOT_FILEIDX_MODE_SYMLINK:
+			ct->mode = S_IFLNK;
+			break;
+		default:
+			fprintf(stderr, "got: ie mode is 0x%x\n", ie->mode);
+			err = got_error_path(path, GOT_ERR_BAD_FILETYPE);
+			goto done;
+		}
+		ct->mode |= got_fileindex_entry_perms_get(ie);
+	} else if (status != GOT_STATUS_DELETE &&
+	    staged_status != GOT_STATUS_DELETE) {
 		if (dirfd != -1) {
 			if (fstatat(dirfd, de_name, &sb,
 			    AT_SYMLINK_NOFOLLOW) == -1) {
@@ -5026,7 +5045,7 @@ 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)
+    struct got_fileindex_entry *ie, struct got_repository *repo)
 {
 	const struct got_error *err = NULL;
 	struct got_blob_object *blob = NULL;
@@ -5035,6 +5054,15 @@ reinstall_symlink_after_commit(int *is_bad_symlink, st
 	struct got_tree_object *tree = NULL;
 	struct got_tree_entry *te;
 	char *entry_name;
+	unsigned char status;
+	struct stat sb;
+
+	err = get_file_status(&status, &sb, ie, ct->ondisk_path,
+	    -1, NULL, repo);
+	if (err)
+		return err;
+	if (status != GOT_STATUS_NO_CHANGE)
+		return NULL;
 
 	if (ct->staged_status == GOT_STATUS_ADD ||
 	    ct->staged_status == GOT_STATUS_MODIFY) {
@@ -5117,12 +5145,16 @@ reinstall_symlinks_after_commit(struct got_pathlist_he
 		    ct->status == GOT_STATUS_DELETE)
 			continue;
 
+		ie = got_fileindex_entry_get(fileindex, ct->path,
+		    strlen(ct->path));
+		if (ie == NULL) {
+			err = got_error_path(ct->path, GOT_ERR_BAD_PATH);
+			break;
+		}
 		err = reinstall_symlink_after_commit(&is_bad_symlink,
-		    ct, new_base_commit_id, worktree, repo);
+		    ct, new_base_commit_id, worktree, ie, repo);
 		if (err)
 			break;
-		ie = got_fileindex_entry_get(fileindex, ct->path,
-		    strlen(ct->path));
 		if (ie && is_bad_symlink) {
 			got_fileindex_entry_filetype_set(ie,
 			    GOT_FILEIDX_MODE_BAD_SYMLINK);
@@ -5467,6 +5499,7 @@ got_worktree_commit(struct got_object_id **new_commit_
 
 	cc_arg.commitable_paths = &commitable_paths;
 	cc_arg.worktree = worktree;
+	cc_arg.fileindex = fileindex;
 	cc_arg.repo = repo;
 	cc_arg.have_staged_files = have_staged_files;
 	TAILQ_FOREACH(pe, paths, entry) {
@@ -7020,6 +7053,7 @@ stage_path(void *arg, unsigned char status,
 	char *ondisk_path = NULL, *path_content = NULL;
 	uint32_t stage;
 	struct got_object_id *new_staged_blob_id = NULL;
+	struct stat sb;
 
 	if (status == GOT_STATUS_UNVERSIONED)
 		return NULL;
@@ -7035,6 +7069,11 @@ stage_path(void *arg, unsigned char status,
 	switch (status) {
 	case GOT_STATUS_ADD:
 	case GOT_STATUS_MODIFY:
+		/* XXX could sb.st_mode be passed in by our caller? */
+		if (lstat(ondisk_path, &sb) == -1) {
+			err = got_error_from_errno2("lstat", ondisk_path);
+			break;
+		}
 		if (a->patch_cb) {
 			if (status == GOT_STATUS_ADD) {
 				int choice = GOT_PATCH_CHOICE_NONE;
@@ -7064,6 +7103,13 @@ stage_path(void *arg, unsigned char status,
 		else
 			stage = GOT_FILEIDX_STAGE_MODIFY;
 		got_fileindex_entry_stage_set(ie, stage);
+		if (S_ISLNK(sb.st_mode)) {
+			got_fileindex_entry_staged_filetype_set(ie,
+			    GOT_FILEIDX_MODE_SYMLINK);
+		} else {
+			got_fileindex_entry_staged_filetype_set(ie,
+			    GOT_FILEIDX_MODE_REGULAR_FILE);
+		}
 		a->staged_something = 1;
 		if (a->status_cb == NULL)
 			break;
blob - 199eaf6bbf1859c535e340a5c5f30777378ba090
blob + cf4a6d0c34ede0224a4b154ee7f65844ddf9f51a
--- regress/cmdline/stage.sh
+++ regress/cmdline/stage.sh
@@ -2401,6 +2401,9 @@ EOF
 		test_done "$testroot" "$ret"
 		return 1
 	fi
+
+	rm $testroot/wt/alpha.link
+	echo 'this is regular file alpha.link' > $testroot/wt/alpha.link
 
 	(cd $testroot/wt && got diff -s > $testroot/stdout)
 
@@ -2542,18 +2545,18 @@ EOF
 		return 1
 	fi
 
-	if ! [ -h $testroot/wt/alpha.link ]; then
-		echo "alpha.link is not a symlink"
+	if [ -h $testroot/wt/alpha.link ]; then
+		echo "alpha.link is a symlink"
 		test_done "$testroot" "1"
 		return 1
 	fi
 
-	readlink $testroot/wt/alpha.link > $testroot/stdout
-	echo "beta" > $testroot/stdout.expected
-	cmp -s $testroot/stdout.expected $testroot/stdout
+	echo 'this is regular file alpha.link' > $testroot/content.expected
+	cp $testroot/wt/alpha.link $testroot/content
+	cmp -s $testroot/content.expected $testroot/content
 	ret="$?"
 	if [ "$ret" != "0" ]; then
-		diff -u $testroot/stdout.expected $testroot/stdout
+		diff -u $testroot/content.expected $testroot/content
 		test_done "$testroot" "$ret"
 		return 1
 	fi
@@ -2563,7 +2566,7 @@ EOF
 		test_done "$testroot" "1"
 		return 1
 	fi
-	echo -n ".got/bar" >> $testroot/content.expected
+	echo -n ".got/bar" > $testroot/content.expected
 	cp $testroot/wt/dotgotbar.link $testroot/content
 	cmp -s $testroot/content.expected $testroot/content
 	ret="$?"