diff options
Diffstat (limited to 'local-fetch.c')
-rw-r--r-- | local-fetch.c | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/local-fetch.c b/local-fetch.c new file mode 100644 index 0000000000..4b650efa8b --- /dev/null +++ b/local-fetch.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2005 Junio C Hamano + */ +#include "cache.h" +#include "commit.h" +#include "fetch.h" + +static int use_link; +static int use_symlink; +static int use_filecopy = 1; +static int commits_on_stdin; + +static const char *path; /* "Remote" git repository */ + +void prefetch(unsigned char *sha1) +{ +} + +static struct packed_git *packs; + +static void setup_index(unsigned char *sha1) +{ + struct packed_git *new_pack; + char filename[PATH_MAX]; + strcpy(filename, path); + strcat(filename, "/objects/pack/pack-"); + strcat(filename, sha1_to_hex(sha1)); + strcat(filename, ".idx"); + new_pack = parse_pack_index_file(sha1, filename); + new_pack->next = packs; + packs = new_pack; +} + +static int setup_indices(void) +{ + DIR *dir; + struct dirent *de; + char filename[PATH_MAX]; + unsigned char sha1[20]; + sprintf(filename, "%s/objects/pack/", path); + dir = opendir(filename); + if (!dir) + return -1; + while ((de = readdir(dir)) != NULL) { + int namelen = strlen(de->d_name); + if (namelen != 50 || + !has_extension(de->d_name, ".pack")) + continue; + get_sha1_hex(de->d_name + 5, sha1); + setup_index(sha1); + } + closedir(dir); + return 0; +} + +static int copy_file(const char *source, char *dest, const char *hex, + int warn_if_not_exists) +{ + safe_create_leading_directories(dest); + if (use_link) { + if (!link(source, dest)) { + pull_say("link %s\n", hex); + return 0; + } + /* If we got ENOENT there is no point continuing. */ + if (errno == ENOENT) { + if (!warn_if_not_exists) + return -1; + return error("does not exist %s", source); + } + } + if (use_symlink) { + struct stat st; + if (stat(source, &st)) { + if (!warn_if_not_exists && errno == ENOENT) + return -1; + return error("cannot stat %s: %s", source, + strerror(errno)); + } + if (!symlink(source, dest)) { + pull_say("symlink %s\n", hex); + return 0; + } + } + if (use_filecopy) { + int ifd, ofd, status = 0; + + ifd = open(source, O_RDONLY); + if (ifd < 0) { + if (!warn_if_not_exists && errno == ENOENT) + return -1; + return error("cannot open %s", source); + } + ofd = open(dest, O_WRONLY | O_CREAT | O_EXCL, 0666); + if (ofd < 0) { + close(ifd); + return error("cannot open %s", dest); + } + status = copy_fd(ifd, ofd); + close(ofd); + if (status) + return error("cannot write %s", dest); + pull_say("copy %s\n", hex); + return 0; + } + return error("failed to copy %s with given copy methods.", hex); +} + +static int fetch_pack(const unsigned char *sha1) +{ + struct packed_git *target; + char filename[PATH_MAX]; + if (setup_indices()) + return -1; + target = find_sha1_pack(sha1, packs); + if (!target) + return error("Couldn't find %s: not separate or in any pack", + sha1_to_hex(sha1)); + if (get_verbosely) { + fprintf(stderr, "Getting pack %s\n", + sha1_to_hex(target->sha1)); + fprintf(stderr, " which contains %s\n", + sha1_to_hex(sha1)); + } + sprintf(filename, "%s/objects/pack/pack-%s.pack", + path, sha1_to_hex(target->sha1)); + copy_file(filename, sha1_pack_name(target->sha1), + sha1_to_hex(target->sha1), 1); + sprintf(filename, "%s/objects/pack/pack-%s.idx", + path, sha1_to_hex(target->sha1)); + copy_file(filename, sha1_pack_index_name(target->sha1), + sha1_to_hex(target->sha1), 1); + install_packed_git(target); + return 0; +} + +static int fetch_file(const unsigned char *sha1) +{ + static int object_name_start = -1; + static char filename[PATH_MAX]; + char *hex = sha1_to_hex(sha1); + char *dest_filename = sha1_file_name(sha1); + + if (object_name_start < 0) { + strcpy(filename, path); /* e.g. git.git */ + strcat(filename, "/objects/"); + object_name_start = strlen(filename); + } + filename[object_name_start+0] = hex[0]; + filename[object_name_start+1] = hex[1]; + filename[object_name_start+2] = '/'; + strcpy(filename + object_name_start + 3, hex + 2); + return copy_file(filename, dest_filename, hex, 0); +} + +int fetch(unsigned char *sha1) +{ + if (has_sha1_file(sha1)) + return 0; + else + return fetch_file(sha1) && fetch_pack(sha1); +} + +int fetch_ref(char *ref, unsigned char *sha1) +{ + static int ref_name_start = -1; + static char filename[PATH_MAX]; + static char hex[41]; + int ifd; + + if (ref_name_start < 0) { + sprintf(filename, "%s/refs/", path); + ref_name_start = strlen(filename); + } + strcpy(filename + ref_name_start, ref); + ifd = open(filename, O_RDONLY); + if (ifd < 0) { + close(ifd); + return error("cannot open %s", filename); + } + if (read_in_full(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) { + close(ifd); + return error("cannot read from %s", filename); + } + close(ifd); + pull_say("ref %s\n", sha1_to_hex(sha1)); + return 0; +} + +static const char local_pull_usage[] = +"git-local-fetch [-c] [-t] [-a] [-v] [-w filename] [--recover] [-l] [-s] [-n] [--stdin] commit-id path"; + +/* + * By default we only use file copy. + * If -l is specified, a hard link is attempted. + * If -s is specified, then a symlink is attempted. + * If -n is _not_ specified, then a regular file-to-file copy is done. + */ +int main(int argc, const char **argv) +{ + int commits; + const char **write_ref = NULL; + char **commit_id; + int arg = 1; + + setup_git_directory(); + git_config(git_default_config); + + while (arg < argc && argv[arg][0] == '-') { + if (argv[arg][1] == 't') + get_tree = 1; + else if (argv[arg][1] == 'c') + get_history = 1; + else if (argv[arg][1] == 'a') { + get_all = 1; + get_tree = 1; + get_history = 1; + } + else if (argv[arg][1] == 'l') + use_link = 1; + else if (argv[arg][1] == 's') + use_symlink = 1; + else if (argv[arg][1] == 'n') + use_filecopy = 0; + else if (argv[arg][1] == 'v') + get_verbosely = 1; + else if (argv[arg][1] == 'w') + write_ref = &argv[++arg]; + else if (!strcmp(argv[arg], "--recover")) + get_recover = 1; + else if (!strcmp(argv[arg], "--stdin")) + commits_on_stdin = 1; + else + usage(local_pull_usage); + arg++; + } + if (argc < arg + 2 - commits_on_stdin) + usage(local_pull_usage); + if (commits_on_stdin) { + commits = pull_targets_stdin(&commit_id, &write_ref); + } else { + commit_id = (char **) &argv[arg++]; + commits = 1; + } + path = argv[arg]; + + if (pull(commits, commit_id, write_ref, path)) + return 1; + + if (commits_on_stdin) + pull_targets_free(commits, commit_id, write_ref); + + return 0; +} |