Commit 6be5ac2351d7ff2589e3a9c7ee3fefc99da48500

Patrick Steinhardt 2019-07-11T15:30:51

checkout: postpone creation of symlinks to the end On most platforms it's fine to create symlinks to nonexisting files. Not so on Windows, where the type of a symlink (file or directory) needs to be set at creation time. So depending on whether the target file exists or not, we may end up with different symlink types. This creates a problem when performing checkouts, where we simply iterate over all blobs that need to be updated without treating symlinks any special. If the target file of the symlink is going to be checked out after the symlink itself, then the symlink will be created as directory symlink and not as file symlink. Fix the issue by iterating over blobs twice: once to perform postponed deletions and updates to non-symlink blobs, and once to perform updates to symlink blobs.

diff --git a/src/checkout.c b/src/checkout.c
index 730ec62..d618e3d 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -1893,11 +1893,18 @@ static int checkout_create_the_new(
 				return error;
 		}
 
-		if (actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) {
-			error = checkout_blob(data, &delta->new_file);
-			if (error < 0)
+		if (actions[i] & CHECKOUT_ACTION__UPDATE_BLOB && !S_ISLNK(delta->new_file.mode)) {
+			if ((error = checkout_blob(data, &delta->new_file)) < 0)
 				return error;
+			data->completed_steps++;
+			report_progress(data, delta->new_file.path);
+		}
+	}
 
+	git_vector_foreach(&data->diff->deltas, i, delta) {
+		if (actions[i] & CHECKOUT_ACTION__UPDATE_BLOB && S_ISLNK(delta->new_file.mode)) {
+			if ((error = checkout_blob(data, &delta->new_file)) < 0)
+				return error;
 			data->completed_steps++;
 			report_progress(data, delta->new_file.path);
 		}