Make fiel checkout function available to the git library
The merge stuff will want it soon, and we don't want to duplicate all the work..maint
							parent
							
								
									76f38347b4
								
							
						
					
					
						commit
						12dccc1654
					
				
							
								
								
									
										2
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										2
									
								
								Makefile
								
								
								
								
							|  | @ -40,7 +40,7 @@ install: $(PROG) $(SCRIPTS) | |||
| 	$(INSTALL) $(PROG) $(SCRIPTS) $(dest)$(bin) | ||||
|  | ||||
| LIB_OBJS=read-cache.o sha1_file.o usage.o object.o commit.o tree.o blob.o \ | ||||
| 	 tag.o delta.o date.o index.o diff-delta.o patch-delta.o | ||||
| 	 tag.o delta.o date.o index.o diff-delta.o patch-delta.o entry.o | ||||
| LIB_FILE=libgit.a | ||||
| LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h | ||||
|  | ||||
|  |  | |||
							
								
								
									
										11
									
								
								cache.h
								
								
								
								
							
							
						
						
									
										11
									
								
								cache.h
								
								
								
								
							|  | @ -214,4 +214,15 @@ static inline void *xcalloc(size_t nmemb, size_t size) | |||
| 	return ret; | ||||
| } | ||||
|  | ||||
| struct checkout { | ||||
| 	const char *base_dir; | ||||
| 	int base_dir_len; | ||||
| 	unsigned force:1, | ||||
| 		 quiet:1, | ||||
| 		 not_new:1, | ||||
| 		 refresh_cache:1; | ||||
| }; | ||||
|  | ||||
| extern int checkout_entry(struct cache_entry *ce, struct checkout *state); | ||||
|  | ||||
| #endif /* CACHE_H */ | ||||
|  |  | |||
							
								
								
									
										193
									
								
								checkout-cache.c
								
								
								
								
							
							
						
						
									
										193
									
								
								checkout-cache.c
								
								
								
								
							|  | @ -32,175 +32,22 @@ | |||
|  * of "-a" causing problems (not possible in the above example, | ||||
|  * but get used to it in scripting!). | ||||
|  */ | ||||
| #include <sys/types.h> | ||||
| #include <dirent.h> | ||||
| #include "cache.h" | ||||
|  | ||||
| static int force = 0, quiet = 0, not_new = 0, refresh_cache = 0; | ||||
| static const char *base_dir = ""; | ||||
| static int base_dir_len = 0; | ||||
|  | ||||
| static void create_directories(const char *path) | ||||
| { | ||||
| 	int len = strlen(path); | ||||
| 	char *buf = xmalloc(len + 1); | ||||
| 	const char *slash = path; | ||||
|  | ||||
| 	while ((slash = strchr(slash+1, '/')) != NULL) { | ||||
| 		len = slash - path; | ||||
| 		memcpy(buf, path, len); | ||||
| 		buf[len] = 0; | ||||
| 		if (mkdir(buf, 0755)) { | ||||
| 			if (errno == EEXIST) { | ||||
| 				struct stat st; | ||||
| 				if (len > base_dir_len && force && !unlink(buf) && !mkdir(buf, 0755)) | ||||
| 					continue; | ||||
| 				if (!stat(buf, &st) && S_ISDIR(st.st_mode)) | ||||
| 					continue; /* ok */ | ||||
| 			} | ||||
| 			die("cannot create directory at %s", buf); | ||||
| 		} | ||||
| 	} | ||||
| 	free(buf); | ||||
| } | ||||
|  | ||||
| static void remove_subtree(const char *path) | ||||
| { | ||||
| 	DIR *dir = opendir(path); | ||||
| 	struct dirent *de; | ||||
| 	char pathbuf[PATH_MAX]; | ||||
| 	char *name; | ||||
| 	 | ||||
| 	if (!dir) | ||||
| 		die("cannot opendir %s", path); | ||||
| 	strcpy(pathbuf, path); | ||||
| 	name = pathbuf + strlen(path); | ||||
| 	*name++ = '/'; | ||||
| 	while ((de = readdir(dir)) != NULL) { | ||||
| 		struct stat st; | ||||
| 		if ((de->d_name[0] == '.') && | ||||
| 		    ((de->d_name[1] == 0) || | ||||
| 		     ((de->d_name[1] == '.') && de->d_name[2] == 0))) | ||||
| 			continue; | ||||
| 		strcpy(name, de->d_name); | ||||
| 		if (lstat(pathbuf, &st)) | ||||
| 			die("cannot lstat %s", pathbuf); | ||||
| 		if (S_ISDIR(st.st_mode)) | ||||
| 			remove_subtree(pathbuf); | ||||
| 		else if (unlink(pathbuf)) | ||||
| 			die("cannot unlink %s", pathbuf); | ||||
| 	} | ||||
| 	closedir(dir); | ||||
| 	if (rmdir(path)) | ||||
| 		die("cannot rmdir %s", path); | ||||
| } | ||||
|  | ||||
| static int create_file(const char *path, unsigned int mode) | ||||
| { | ||||
| 	int fd; | ||||
|  | ||||
| 	mode = (mode & 0100) ? 0777 : 0666; | ||||
| 	create_directories(path); | ||||
| 	fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); | ||||
| 	if (fd < 0) { | ||||
| 		if (errno == EISDIR && force) { | ||||
| 			remove_subtree(path); | ||||
| 			fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); | ||||
| 		} | ||||
| 	} | ||||
| 	return fd; | ||||
| } | ||||
|  | ||||
| static int write_entry(struct cache_entry *ce, const char *path) | ||||
| { | ||||
| 	int fd; | ||||
| 	void *new; | ||||
| 	unsigned long size; | ||||
| 	long wrote; | ||||
| 	char type[20]; | ||||
| 	char target[1024]; | ||||
|  | ||||
| 	new = read_sha1_file(ce->sha1, type, &size); | ||||
| 	if (!new || strcmp(type, "blob")) { | ||||
| 		if (new) | ||||
| 			free(new); | ||||
| 		return error("git-checkout-cache: unable to read sha1 file of %s (%s)", | ||||
| 			path, sha1_to_hex(ce->sha1)); | ||||
| 	} | ||||
| 	switch (ntohl(ce->ce_mode) & S_IFMT) { | ||||
| 	case S_IFREG: | ||||
| 		fd = create_file(path, ntohl(ce->ce_mode)); | ||||
| 		if (fd < 0) { | ||||
| 			free(new); | ||||
| 			return error("git-checkout-cache: unable to create file %s (%s)", | ||||
| 				path, strerror(errno)); | ||||
| 		} | ||||
| 		wrote = write(fd, new, size); | ||||
| 		close(fd); | ||||
| 		free(new); | ||||
| 		if (wrote != size) | ||||
| 			return error("git-checkout-cache: unable to write file %s", path); | ||||
| 		break; | ||||
| 	case S_IFLNK: | ||||
| 		memcpy(target, new, size); | ||||
| 		target[size] = '\0'; | ||||
| 		create_directories(path); | ||||
| 		if (symlink(target, path)) { | ||||
| 			free(new); | ||||
| 			return error("git-checkout-cache: unable to create symlink %s (%s)", | ||||
| 				path, strerror(errno)); | ||||
| 		} | ||||
| 		free(new); | ||||
| 		break; | ||||
| 	default: | ||||
| 		free(new); | ||||
| 		return error("git-checkout-cache: unknown file mode for %s", path); | ||||
| 	} | ||||
|  | ||||
| 	if (refresh_cache) { | ||||
| 		struct stat st; | ||||
| 		lstat(ce->name, &st); | ||||
| 		fill_stat_cache_info(ce, &st); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int checkout_entry(struct cache_entry *ce) | ||||
| { | ||||
| 	struct stat st; | ||||
| 	static char path[MAXPATHLEN+1]; | ||||
| 	int len = base_dir_len; | ||||
|  | ||||
| 	memcpy(path, base_dir, len); | ||||
| 	strcpy(path + len, ce->name); | ||||
|  | ||||
| 	if (!lstat(path, &st)) { | ||||
| 		unsigned changed = ce_match_stat(ce, &st); | ||||
| 		if (!changed) | ||||
| 			return 0; | ||||
| 		if (!force) { | ||||
| 			if (!quiet) | ||||
| 				fprintf(stderr, "git-checkout-cache: %s already exists\n", path); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		/* | ||||
| 		 * We unlink the old file, to get the new one with the | ||||
| 		 * right permissions (including umask, which is nasty | ||||
| 		 * to emulate by hand - much easier to let the system | ||||
| 		 * just do the right thing) | ||||
| 		 */ | ||||
| 		unlink(path); | ||||
| 	} else if (not_new)  | ||||
| 		return 0; | ||||
| 	return write_entry(ce, path); | ||||
| } | ||||
| static struct checkout state = { | ||||
| 	.base_dir = "", | ||||
| 	.base_dir_len = 0, | ||||
| 	.force = 0, | ||||
| 	.quiet = 0, | ||||
| 	.not_new = 0, | ||||
| 	.refresh_cache = 0, | ||||
| }; | ||||
|  | ||||
| static int checkout_file(const char *name) | ||||
| { | ||||
| 	int pos = cache_name_pos(name, strlen(name)); | ||||
| 	if (pos < 0) { | ||||
| 		if (!quiet) { | ||||
| 		if (!state.quiet) { | ||||
| 			pos = -pos - 1; | ||||
| 			fprintf(stderr, | ||||
| 				"git-checkout-cache: %s is %s.\n", | ||||
|  | @ -211,7 +58,7 @@ static int checkout_file(const char *name) | |||
| 		} | ||||
| 		return -1; | ||||
| 	} | ||||
| 	return checkout_entry(active_cache[pos]); | ||||
| 	return checkout_entry(active_cache[pos], &state); | ||||
| } | ||||
|  | ||||
| static int checkout_all(void) | ||||
|  | @ -222,7 +69,7 @@ static int checkout_all(void) | |||
| 		struct cache_entry *ce = active_cache[i]; | ||||
| 		if (ce_stage(ce)) | ||||
| 			continue; | ||||
| 		if (checkout_entry(ce) < 0) | ||||
| 		if (checkout_entry(ce, &state) < 0) | ||||
| 			return -1; | ||||
| 	} | ||||
| 	return 0; | ||||
|  | @ -250,19 +97,19 @@ int main(int argc, char **argv) | |||
| 				continue; | ||||
| 			} | ||||
| 			if (!strcmp(arg, "-f")) { | ||||
| 				force = 1; | ||||
| 				state.force = 1; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (!strcmp(arg, "-q")) { | ||||
| 				quiet = 1; | ||||
| 				state.quiet = 1; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (!strcmp(arg, "-n")) { | ||||
| 				not_new = 1; | ||||
| 				state.not_new = 1; | ||||
| 				continue; | ||||
| 			} | ||||
| 			if (!strcmp(arg, "-u")) { | ||||
| 				refresh_cache = 1; | ||||
| 				state.refresh_cache = 1; | ||||
| 				if (newfd < 0) | ||||
| 					newfd = hold_index_file_for_update | ||||
| 						(&cache_file, | ||||
|  | @ -272,20 +119,20 @@ int main(int argc, char **argv) | |||
| 				continue; | ||||
| 			} | ||||
| 			if (!memcmp(arg, "--prefix=", 9)) { | ||||
| 				base_dir = arg+9; | ||||
| 				base_dir_len = strlen(base_dir); | ||||
| 				state.base_dir = arg+9; | ||||
| 				state.base_dir_len = strlen(state.base_dir); | ||||
| 				continue; | ||||
| 			} | ||||
| 		} | ||||
| 		if (base_dir_len) { | ||||
| 		if (state.base_dir_len) { | ||||
| 			/* when --prefix is specified we do not | ||||
| 			 * want to update cache. | ||||
| 			 */ | ||||
| 			if (refresh_cache) { | ||||
| 			if (state.refresh_cache) { | ||||
| 				close(newfd); newfd = -1; | ||||
| 				rollback_index_file(&cache_file); | ||||
| 			} | ||||
| 			refresh_cache = 0; | ||||
| 			state.refresh_cache = 0; | ||||
| 		} | ||||
| 		checkout_file(arg); | ||||
| 	} | ||||
|  |  | |||
|  | @ -0,0 +1,160 @@ | |||
| #include <sys/types.h> | ||||
| #include <dirent.h> | ||||
| #include "cache.h" | ||||
|  | ||||
| static void create_directories(const char *path, struct checkout *state) | ||||
| { | ||||
| 	int len = strlen(path); | ||||
| 	char *buf = xmalloc(len + 1); | ||||
| 	const char *slash = path; | ||||
|  | ||||
| 	while ((slash = strchr(slash+1, '/')) != NULL) { | ||||
| 		len = slash - path; | ||||
| 		memcpy(buf, path, len); | ||||
| 		buf[len] = 0; | ||||
| 		if (mkdir(buf, 0755)) { | ||||
| 			if (errno == EEXIST) { | ||||
| 				struct stat st; | ||||
| 				if (len > state->base_dir_len && state->force && !unlink(buf) && !mkdir(buf, 0755)) | ||||
| 					continue; | ||||
| 				if (!stat(buf, &st) && S_ISDIR(st.st_mode)) | ||||
| 					continue; /* ok */ | ||||
| 			} | ||||
| 			die("cannot create directory at %s", buf); | ||||
| 		} | ||||
| 	} | ||||
| 	free(buf); | ||||
| } | ||||
|  | ||||
| static void remove_subtree(const char *path) | ||||
| { | ||||
| 	DIR *dir = opendir(path); | ||||
| 	struct dirent *de; | ||||
| 	char pathbuf[PATH_MAX]; | ||||
| 	char *name; | ||||
| 	 | ||||
| 	if (!dir) | ||||
| 		die("cannot opendir %s", path); | ||||
| 	strcpy(pathbuf, path); | ||||
| 	name = pathbuf + strlen(path); | ||||
| 	*name++ = '/'; | ||||
| 	while ((de = readdir(dir)) != NULL) { | ||||
| 		struct stat st; | ||||
| 		if ((de->d_name[0] == '.') && | ||||
| 		    ((de->d_name[1] == 0) || | ||||
| 		     ((de->d_name[1] == '.') && de->d_name[2] == 0))) | ||||
| 			continue; | ||||
| 		strcpy(name, de->d_name); | ||||
| 		if (lstat(pathbuf, &st)) | ||||
| 			die("cannot lstat %s", pathbuf); | ||||
| 		if (S_ISDIR(st.st_mode)) | ||||
| 			remove_subtree(pathbuf); | ||||
| 		else if (unlink(pathbuf)) | ||||
| 			die("cannot unlink %s", pathbuf); | ||||
| 	} | ||||
| 	closedir(dir); | ||||
| 	if (rmdir(path)) | ||||
| 		die("cannot rmdir %s", path); | ||||
| } | ||||
|  | ||||
| static int create_file(const char *path, unsigned int mode, int force) | ||||
| { | ||||
| 	int fd; | ||||
|  | ||||
| 	mode = (mode & 0100) ? 0777 : 0666; | ||||
| 	fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); | ||||
| 	if (fd < 0) { | ||||
| 		if (errno == EISDIR && force) { | ||||
| 			remove_subtree(path); | ||||
| 			fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode); | ||||
| 		} | ||||
| 	} | ||||
| 	return fd; | ||||
| } | ||||
|  | ||||
| static int write_entry(struct cache_entry *ce, const char *path, struct checkout *state) | ||||
| { | ||||
| 	int fd; | ||||
| 	void *new; | ||||
| 	unsigned long size; | ||||
| 	long wrote; | ||||
| 	char type[20]; | ||||
| 	char target[1024]; | ||||
|  | ||||
| 	new = read_sha1_file(ce->sha1, type, &size); | ||||
| 	if (!new || strcmp(type, "blob")) { | ||||
| 		if (new) | ||||
| 			free(new); | ||||
| 		return error("git-checkout-cache: unable to read sha1 file of %s (%s)", | ||||
| 			path, sha1_to_hex(ce->sha1)); | ||||
| 	} | ||||
| 	switch (ntohl(ce->ce_mode) & S_IFMT) { | ||||
| 	case S_IFREG: | ||||
| 		fd = create_file(path, ntohl(ce->ce_mode), state->force); | ||||
| 		if (fd < 0) { | ||||
| 			free(new); | ||||
| 			return error("git-checkout-cache: unable to create file %s (%s)", | ||||
| 				path, strerror(errno)); | ||||
| 		} | ||||
| 		wrote = write(fd, new, size); | ||||
| 		close(fd); | ||||
| 		free(new); | ||||
| 		if (wrote != size) | ||||
| 			return error("git-checkout-cache: unable to write file %s", path); | ||||
| 		break; | ||||
| 	case S_IFLNK: | ||||
| 		memcpy(target, new, size); | ||||
| 		target[size] = '\0'; | ||||
| 		if (symlink(target, path)) { | ||||
| 			free(new); | ||||
| 			return error("git-checkout-cache: unable to create symlink %s (%s)", | ||||
| 				path, strerror(errno)); | ||||
| 		} | ||||
| 		free(new); | ||||
| 		break; | ||||
| 	default: | ||||
| 		free(new); | ||||
| 		return error("git-checkout-cache: unknown file mode for %s", path); | ||||
| 	} | ||||
|  | ||||
| 	if (state->update) { | ||||
| 		struct stat st; | ||||
| 		lstat(ce->name, &st); | ||||
| 		fill_stat_cache_info(ce, &st); | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int checkout_entry(struct cache_entry *ce, struct checkout *state) | ||||
| { | ||||
| 	struct stat st; | ||||
| 	static char path[MAXPATHLEN+1]; | ||||
| 	int len = state->base_dir_len; | ||||
|  | ||||
| 	memcpy(path, state->base_dir, len); | ||||
| 	strcpy(path + len, ce->name); | ||||
|  | ||||
| 	if (!lstat(path, &st)) { | ||||
| 		unsigned changed = ce_match_stat(ce, &st); | ||||
| 		if (!changed) | ||||
| 			return 0; | ||||
| 		if (!state->force) { | ||||
| 			if (!state->quiet) | ||||
| 				fprintf(stderr, "git-checkout-cache: %s already exists\n", path); | ||||
| 			return 0; | ||||
| 		} | ||||
|  | ||||
| 		/* | ||||
| 		 * We unlink the old file, to get the new one with the | ||||
| 		 * right permissions (including umask, which is nasty | ||||
| 		 * to emulate by hand - much easier to let the system | ||||
| 		 * just do the right thing) | ||||
| 		 */ | ||||
| 		unlink(path); | ||||
| 	} else if (state->not_new)  | ||||
| 		return 0; | ||||
| 	create_directories(path, state); | ||||
| 	return write_entry(ce, path, state); | ||||
| } | ||||
|  | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	 Linus Torvalds
						Linus Torvalds