Merge branch 'fixes'
[git] / local-fetch.c
1 /*
2  * Copyright (C) 2005 Junio C Hamano
3  */
4 #include "cache.h"
5 #include "commit.h"
6 #include "fetch.h"
7
8 static int use_link = 0;
9 static int use_symlink = 0;
10 static int use_filecopy = 1;
11
12 static char *path; /* "Remote" git repository */
13
14 void prefetch(unsigned char *sha1)
15 {
16 }
17
18 static struct packed_git *packs = NULL;
19
20 static void setup_index(unsigned char *sha1)
21 {
22         struct packed_git *new_pack;
23         char filename[PATH_MAX];
24         strcpy(filename, path);
25         strcat(filename, "/objects/pack/pack-");
26         strcat(filename, sha1_to_hex(sha1));
27         strcat(filename, ".idx");
28         new_pack = parse_pack_index_file(sha1, filename);
29         new_pack->next = packs;
30         packs = new_pack;
31 }
32
33 static int setup_indices(void)
34 {
35         DIR *dir;
36         struct dirent *de;
37         char filename[PATH_MAX];
38         unsigned char sha1[20];
39         sprintf(filename, "%s/objects/pack/", path);
40         dir = opendir(filename);
41         if (!dir)
42                 return -1;
43         while ((de = readdir(dir)) != NULL) {
44                 int namelen = strlen(de->d_name);
45                 if (namelen != 50 || 
46                     strcmp(de->d_name + namelen - 5, ".pack"))
47                         continue;
48                 get_sha1_hex(de->d_name + 5, sha1);
49                 setup_index(sha1);
50         }
51         closedir(dir);
52         return 0;
53 }
54
55 static int copy_file(const char *source, const char *dest, const char *hex,
56                      int warn_if_not_exists)
57 {
58         if (use_link) {
59                 if (!link(source, dest)) {
60                         pull_say("link %s\n", hex);
61                         return 0;
62                 }
63                 /* If we got ENOENT there is no point continuing. */
64                 if (errno == ENOENT) {
65                         if (warn_if_not_exists)
66                                 fprintf(stderr, "does not exist %s\n", source);
67                         return -1;
68                 }
69         }
70         if (use_symlink) {
71                 struct stat st;
72                 if (stat(source, &st)) {
73                         if (!warn_if_not_exists && errno == ENOENT)
74                                 return -1;
75                         fprintf(stderr, "cannot stat %s: %s\n", source,
76                                 strerror(errno));
77                         return -1;
78                 }
79                 if (!symlink(source, dest)) {
80                         pull_say("symlink %s\n", hex);
81                         return 0;
82                 }
83         }
84         if (use_filecopy) {
85                 int ifd, ofd, status;
86                 struct stat st;
87                 void *map;
88                 ifd = open(source, O_RDONLY);
89                 if (ifd < 0 || fstat(ifd, &st) < 0) {
90                         int err = errno;
91                         if (ifd >= 0)
92                                 close(ifd);
93                         if (!warn_if_not_exists && err == ENOENT)
94                                 return -1;
95                         fprintf(stderr, "cannot open %s\n", source);
96                         return -1;
97                 }
98                 map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, ifd, 0);
99                 close(ifd);
100                 if (map == MAP_FAILED) {
101                         fprintf(stderr, "cannot mmap %s\n", source);
102                         return -1;
103                 }
104                 ofd = open(dest, O_WRONLY | O_CREAT | O_EXCL, 0666);
105                 status = ((ofd < 0) ||
106                           (write(ofd, map, st.st_size) != st.st_size));
107                 munmap(map, st.st_size);
108                 if (ofd >= 0)
109                         close(ofd);
110                 if (status)
111                         fprintf(stderr, "cannot write %s\n", dest);
112                 else
113                         pull_say("copy %s\n", hex);
114                 return status;
115         }
116         fprintf(stderr, "failed to copy %s with given copy methods.\n", hex);
117         return -1;
118 }
119
120 static int fetch_pack(const unsigned char *sha1)
121 {
122         struct packed_git *target;
123         char filename[PATH_MAX];
124         if (setup_indices())
125                 return -1;
126         target = find_sha1_pack(sha1, packs);
127         if (!target)
128                 return error("Couldn't find %s: not separate or in any pack", 
129                              sha1_to_hex(sha1));
130         if (get_verbosely) {
131                 fprintf(stderr, "Getting pack %s\n",
132                         sha1_to_hex(target->sha1));
133                 fprintf(stderr, " which contains %s\n",
134                         sha1_to_hex(sha1));
135         }
136         sprintf(filename, "%s/objects/pack/pack-%s.pack", 
137                 path, sha1_to_hex(target->sha1));
138         copy_file(filename, sha1_pack_name(target->sha1),
139                   sha1_to_hex(target->sha1), 1);
140         sprintf(filename, "%s/objects/pack/pack-%s.idx", 
141                 path, sha1_to_hex(target->sha1));
142         copy_file(filename, sha1_pack_index_name(target->sha1),
143                   sha1_to_hex(target->sha1), 1);
144         install_packed_git(target);
145         return 0;
146 }
147
148 static int fetch_file(const unsigned char *sha1)
149 {
150         static int object_name_start = -1;
151         static char filename[PATH_MAX];
152         char *hex = sha1_to_hex(sha1);
153         const char *dest_filename = sha1_file_name(sha1);
154
155         if (object_name_start < 0) {
156                 strcpy(filename, path); /* e.g. git.git */
157                 strcat(filename, "/objects/");
158                 object_name_start = strlen(filename);
159         }
160         filename[object_name_start+0] = hex[0];
161         filename[object_name_start+1] = hex[1];
162         filename[object_name_start+2] = '/';
163         strcpy(filename + object_name_start + 3, hex + 2);
164         return copy_file(filename, dest_filename, hex, 0);
165 }
166
167 int fetch(unsigned char *sha1)
168 {
169         if (has_sha1_file(sha1))
170                 return 0;
171         else
172                 return fetch_file(sha1) && fetch_pack(sha1);
173 }
174
175 int fetch_ref(char *ref, unsigned char *sha1)
176 {
177         static int ref_name_start = -1;
178         static char filename[PATH_MAX];
179         static char hex[41];
180         int ifd;
181
182         if (ref_name_start < 0) {
183                 sprintf(filename, "%s/refs/", path);
184                 ref_name_start = strlen(filename);
185         }
186         strcpy(filename + ref_name_start, ref);
187         ifd = open(filename, O_RDONLY);
188         if (ifd < 0) {
189                 close(ifd);
190                 fprintf(stderr, "cannot open %s\n", filename);
191                 return -1;
192         }
193         if (read(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) {
194                 close(ifd);
195                 fprintf(stderr, "cannot read from %s\n", filename);
196                 return -1;
197         }
198         close(ifd);
199         pull_say("ref %s\n", sha1_to_hex(sha1));
200         return 0;
201 }
202
203 static const char local_pull_usage[] =
204 "git-local-fetch [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [-l] [-s] [-n] commit-id path";
205
206 /* 
207  * By default we only use file copy.
208  * If -l is specified, a hard link is attempted.
209  * If -s is specified, then a symlink is attempted.
210  * If -n is _not_ specified, then a regular file-to-file copy is done.
211  */
212 int main(int argc, char **argv)
213 {
214         char *commit_id;
215         int arg = 1;
216
217         while (arg < argc && argv[arg][0] == '-') {
218                 if (argv[arg][1] == 't')
219                         get_tree = 1;
220                 else if (argv[arg][1] == 'c')
221                         get_history = 1;
222                 else if (argv[arg][1] == 'a') {
223                         get_all = 1;
224                         get_tree = 1;
225                         get_history = 1;
226                 }
227                 else if (argv[arg][1] == 'l')
228                         use_link = 1;
229                 else if (argv[arg][1] == 's')
230                         use_symlink = 1;
231                 else if (argv[arg][1] == 'n')
232                         use_filecopy = 0;
233                 else if (argv[arg][1] == 'v')
234                         get_verbosely = 1;
235                 else if (argv[arg][1] == 'w')
236                         write_ref = argv[++arg];
237                 else if (!strcmp(argv[arg], "--recover"))
238                         get_recover = 1;
239                 else
240                         usage(local_pull_usage);
241                 arg++;
242         }
243         if (argc < arg + 2)
244                 usage(local_pull_usage);
245         commit_id = argv[arg];
246         path = argv[arg + 1];
247
248         if (pull(commit_id))
249                 return 1;
250
251         return 0;
252 }