]>
Commit | Line | Data |
---|---|---|
764df57e BS |
1 | /* |
2 | * Copyright (C) 2009-2012 the libgit2 contributors | |
3 | * | |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with | |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
6 | */ | |
7 | ||
8 | #include <assert.h> | |
830388a7 | 9 | |
764df57e BS |
10 | #include "git2/clone.h" |
11 | #include "git2/remote.h" | |
bb1f6087 | 12 | #include "git2/revparse.h" |
4fbc899a BS |
13 | #include "git2/branch.h" |
14 | #include "git2/config.h" | |
14741d62 | 15 | #include "git2/checkout.h" |
2b63db4c BS |
16 | #include "git2/commit.h" |
17 | #include "git2/tree.h" | |
764df57e BS |
18 | |
19 | #include "common.h" | |
20 | #include "remote.h" | |
21 | #include "fileops.h" | |
4fbc899a | 22 | #include "refs.h" |
c3b5099f | 23 | #include "path.h" |
764df57e | 24 | |
bf0e62a2 | 25 | static int create_branch( |
7eca3c56 | 26 | git_reference **branch, |
27 | git_repository *repo, | |
28 | const git_oid *target, | |
29 | const char *name) | |
4fbc899a | 30 | { |
ea817863 | 31 | git_object *head_obj = NULL; |
8a155a04 | 32 | git_reference *branch_ref; |
bf0e62a2 | 33 | int error; |
ea817863 BS |
34 | |
35 | /* Find the target commit */ | |
bf0e62a2 | 36 | if ((error = git_object_lookup(&head_obj, repo, target, GIT_OBJ_ANY)) < 0) |
37 | return error; | |
ea817863 BS |
38 | |
39 | /* Create the new branch */ | |
bf0e62a2 | 40 | error = git_branch_create(&branch_ref, repo, name, head_obj, 0); |
ea817863 BS |
41 | |
42 | git_object_free(head_obj); | |
7eca3c56 | 43 | |
bf0e62a2 | 44 | if (!error) |
7eca3c56 | 45 | *branch = branch_ref; |
46 | else | |
47 | git_reference_free(branch_ref); | |
48 | ||
bf0e62a2 | 49 | return error; |
50 | } | |
51 | ||
52 | static int setup_tracking_config( | |
53 | git_repository *repo, | |
54 | const char *branch_name, | |
55 | const char *remote_name, | |
56 | const char *merge_target) | |
57 | { | |
58 | git_config *cfg; | |
59 | git_buf remote_key = GIT_BUF_INIT, merge_key = GIT_BUF_INIT; | |
60 | int error = -1; | |
61 | ||
62 | if (git_repository_config__weakptr(&cfg, repo) < 0) | |
63 | return -1; | |
64 | ||
65 | if (git_buf_printf(&remote_key, "branch.%s.remote", branch_name) < 0) | |
66 | goto cleanup; | |
67 | ||
68 | if (git_buf_printf(&merge_key, "branch.%s.merge", branch_name) < 0) | |
69 | goto cleanup; | |
70 | ||
71 | if (git_config_set_string(cfg, git_buf_cstr(&remote_key), remote_name) < 0) | |
72 | goto cleanup; | |
73 | ||
74 | if (git_config_set_string(cfg, git_buf_cstr(&merge_key), merge_target) < 0) | |
75 | goto cleanup; | |
76 | ||
77 | error = 0; | |
78 | ||
79 | cleanup: | |
80 | git_buf_free(&remote_key); | |
81 | git_buf_free(&merge_key); | |
82 | return error; | |
83 | } | |
84 | ||
85 | static int create_tracking_branch( | |
86 | git_reference **branch, | |
87 | git_repository *repo, | |
88 | const git_oid *target, | |
89 | const char *branch_name) | |
90 | { | |
91 | int error; | |
92 | ||
93 | if ((error = create_branch(branch, repo, target, branch_name)) < 0) | |
94 | return error; | |
95 | ||
96 | return setup_tracking_config( | |
97 | repo, | |
98 | branch_name, | |
99 | GIT_REMOTE_ORIGIN, | |
100 | git_reference_name(*branch)); | |
4fbc899a BS |
101 | } |
102 | ||
70edc1b0 | 103 | struct head_info { |
104 | git_repository *repo; | |
105 | git_oid remote_head_oid; | |
106 | git_buf branchname; | |
d280c71b | 107 | const git_refspec *refspec; |
70edc1b0 | 108 | }; |
109 | ||
d280c71b | 110 | static int reference_matches_remote_head( |
111 | const char *reference_name, | |
112 | void *payload) | |
4fbc899a | 113 | { |
70edc1b0 | 114 | struct head_info *head_info = (struct head_info *)payload; |
ea817863 BS |
115 | git_oid oid; |
116 | ||
d280c71b | 117 | /* TODO: Should we guard against references |
118 | * which name doesn't start with refs/heads/ ? | |
119 | */ | |
120 | ||
ea817863 | 121 | /* Stop looking if we've already found a match */ |
d280c71b | 122 | if (git_buf_len(&head_info->branchname) > 0) |
123 | return 0; | |
124 | ||
125 | if (git_reference_name_to_oid( | |
126 | &oid, | |
127 | head_info->repo, | |
128 | reference_name) < 0) { | |
129 | /* TODO: How to handle not found references? | |
130 | */ | |
131 | return -1; | |
132 | } | |
ea817863 | 133 | |
d280c71b | 134 | if (git_oid_cmp(&head_info->remote_head_oid, &oid) == 0) { |
135 | /* Determine the local reference name from the remote tracking one */ | |
136 | if (git_refspec_transform_l( | |
137 | &head_info->branchname, | |
138 | head_info->refspec, | |
139 | reference_name) < 0) | |
140 | return -1; | |
141 | ||
142 | if (git_buf_sets( | |
143 | &head_info->branchname, | |
144 | git_buf_cstr(&head_info->branchname) + strlen(GIT_REFS_HEADS_DIR)) < 0) | |
145 | return -1; | |
ea817863 | 146 | } |
d280c71b | 147 | |
ea817863 | 148 | return 0; |
8340dd5d BS |
149 | } |
150 | ||
bf0e62a2 | 151 | static int update_head_to_new_branch( |
152 | git_repository *repo, | |
153 | const git_oid *target, | |
154 | const char *name) | |
af58ec9e | 155 | { |
7eca3c56 | 156 | git_reference *tracking_branch; |
157 | int error; | |
ea817863 | 158 | |
bf0e62a2 | 159 | if ((error = create_tracking_branch( |
160 | &tracking_branch, | |
161 | repo, | |
162 | target, | |
163 | name)) < 0) | |
164 | return error; | |
ea817863 | 165 | |
7eca3c56 | 166 | error = git_repository_set_head(repo, git_reference_name(tracking_branch)); |
167 | ||
168 | git_reference_free(tracking_branch); | |
169 | ||
170 | return error; | |
af58ec9e BS |
171 | } |
172 | ||
8340dd5d BS |
173 | static int update_head_to_remote(git_repository *repo, git_remote *remote) |
174 | { | |
d280c71b | 175 | int retcode = -1; |
ea817863 | 176 | git_remote_head *remote_head; |
70edc1b0 | 177 | struct head_info head_info; |
d280c71b | 178 | git_buf remote_master_name = GIT_BUF_INIT; |
ea817863 | 179 | |
bf0e62a2 | 180 | /* Did we just clone an empty repository? */ |
181 | if (remote->refs.length == 0) { | |
182 | return setup_tracking_config( | |
183 | repo, | |
184 | "master", | |
185 | GIT_REMOTE_ORIGIN, | |
186 | GIT_REFS_HEADS_MASTER_FILE); | |
187 | } | |
188 | ||
ea817863 BS |
189 | /* Get the remote's HEAD. This is always the first ref in remote->refs. */ |
190 | remote_head = remote->refs.contents[0]; | |
191 | git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid); | |
192 | git_buf_init(&head_info.branchname, 16); | |
193 | head_info.repo = repo; | |
d280c71b | 194 | head_info.refspec = git_remote_fetchspec(remote); |
195 | ||
196 | /* Determine the remote tracking reference name from the local master */ | |
197 | if (git_refspec_transform_r( | |
198 | &remote_master_name, | |
199 | head_info.refspec, | |
200 | GIT_REFS_HEADS_MASTER_FILE) < 0) | |
201 | return -1; | |
202 | ||
203 | /* Check to see if the remote HEAD points to the remote master */ | |
204 | if (reference_matches_remote_head(git_buf_cstr(&remote_master_name), &head_info) < 0) | |
205 | goto cleanup; | |
206 | ||
207 | if (git_buf_len(&head_info.branchname) > 0) { | |
208 | retcode = update_head_to_new_branch( | |
209 | repo, | |
210 | &head_info.remote_head_oid, | |
211 | git_buf_cstr(&head_info.branchname)); | |
212 | ||
213 | goto cleanup; | |
ea817863 | 214 | } |
d280c71b | 215 | |
ea817863 | 216 | /* Not master. Check all the other refs. */ |
d280c71b | 217 | if (git_reference_foreach( |
218 | repo, | |
219 | GIT_REF_LISTALL, | |
220 | reference_matches_remote_head, | |
221 | &head_info) < 0) | |
222 | goto cleanup; | |
223 | ||
224 | if (git_buf_len(&head_info.branchname) > 0) { | |
225 | retcode = update_head_to_new_branch( | |
226 | repo, | |
227 | &head_info.remote_head_oid, | |
228 | git_buf_cstr(&head_info.branchname)); | |
229 | ||
230 | goto cleanup; | |
231 | } else { | |
232 | /* TODO: What should we do if nothing has been found? | |
233 | */ | |
ea817863 BS |
234 | } |
235 | ||
d280c71b | 236 | cleanup: |
237 | git_buf_free(&remote_master_name); | |
ea817863 BS |
238 | git_buf_free(&head_info.branchname); |
239 | return retcode; | |
bb1f6087 BS |
240 | } |
241 | ||
764df57e BS |
242 | /* |
243 | * submodules? | |
764df57e BS |
244 | */ |
245 | ||
bb1f6087 BS |
246 | |
247 | ||
f2a855d5 | 248 | static int setup_remotes_and_fetch(git_repository *repo, |
ea817863 | 249 | const char *origin_url, |
ef9905c9 | 250 | git_indexer_stats *fetch_stats) |
764df57e | 251 | { |
ea817863 BS |
252 | int retcode = GIT_ERROR; |
253 | git_remote *origin = NULL; | |
254 | git_off_t bytes = 0; | |
255 | git_indexer_stats dummy_stats; | |
256 | ||
ef9905c9 | 257 | if (!fetch_stats) fetch_stats = &dummy_stats; |
ea817863 BS |
258 | |
259 | /* Create the "origin" remote */ | |
096d9e94 | 260 | if (!git_remote_add(&origin, repo, GIT_REMOTE_ORIGIN, origin_url)) { |
ea817863 BS |
261 | /* Connect and download everything */ |
262 | if (!git_remote_connect(origin, GIT_DIR_FETCH)) { | |
ef9905c9 | 263 | if (!git_remote_download(origin, &bytes, fetch_stats)) { |
ea817863 | 264 | /* Create "origin/foo" branches for all remote branches */ |
7affe23d | 265 | if (!git_remote_update_tips(origin)) { |
ea817863 BS |
266 | /* Point HEAD to the same ref as the remote's head */ |
267 | if (!update_head_to_remote(repo, origin)) { | |
268 | retcode = 0; | |
269 | } | |
270 | } | |
271 | } | |
272 | git_remote_disconnect(origin); | |
273 | } | |
274 | git_remote_free(origin); | |
275 | } | |
276 | ||
277 | return retcode; | |
764df57e BS |
278 | } |
279 | ||
acdd3d95 | 280 | |
acdd3d95 BS |
281 | static bool path_is_okay(const char *path) |
282 | { | |
ea817863 BS |
283 | /* The path must either not exist, or be an empty directory */ |
284 | if (!git_path_exists(path)) return true; | |
d024419f | 285 | if (!git_path_is_empty_dir(path)) { |
ea817863 BS |
286 | giterr_set(GITERR_INVALID, |
287 | "'%s' exists and is not an empty directory", path); | |
288 | return false; | |
289 | } | |
d024419f | 290 | return true; |
acdd3d95 BS |
291 | } |
292 | ||
293 | ||
bf0e62a2 | 294 | static int clone_internal( |
295 | git_repository **out, | |
296 | const char *origin_url, | |
297 | const char *path, | |
298 | git_indexer_stats *fetch_stats, | |
299 | git_indexer_stats *checkout_stats, | |
300 | git_checkout_opts *checkout_opts, | |
301 | int is_bare) | |
764df57e | 302 | { |
ea817863 BS |
303 | int retcode = GIT_ERROR; |
304 | git_repository *repo = NULL; | |
ef9905c9 BS |
305 | git_indexer_stats dummy_stats; |
306 | ||
307 | if (!fetch_stats) fetch_stats = &dummy_stats; | |
ea817863 BS |
308 | |
309 | if (!path_is_okay(path)) { | |
310 | return GIT_ERROR; | |
311 | } | |
312 | ||
313 | if (!(retcode = git_repository_init(&repo, path, is_bare))) { | |
ef9905c9 | 314 | if ((retcode = setup_remotes_and_fetch(repo, origin_url, fetch_stats)) < 0) { |
ea817863 BS |
315 | /* Failed to fetch; clean up */ |
316 | git_repository_free(repo); | |
317 | git_futils_rmdir_r(path, GIT_DIRREMOVAL_FILES_AND_DIRS); | |
318 | } else { | |
319 | *out = repo; | |
320 | retcode = 0; | |
321 | } | |
322 | } | |
323 | ||
bf0e62a2 | 324 | if (!retcode && !is_bare && !git_repository_head_orphan(repo)) |
325 | retcode = git_checkout_head(*out, checkout_opts, checkout_stats); | |
326 | ||
ea817863 | 327 | return retcode; |
764df57e BS |
328 | } |
329 | ||
f2a855d5 | 330 | int git_clone_bare(git_repository **out, |
ea817863 BS |
331 | const char *origin_url, |
332 | const char *dest_path, | |
ef9905c9 | 333 | git_indexer_stats *fetch_stats) |
bb1f6087 | 334 | { |
ea817863 | 335 | assert(out && origin_url && dest_path); |
bf0e62a2 | 336 | |
337 | return clone_internal( | |
338 | out, | |
339 | origin_url, | |
340 | dest_path, | |
341 | fetch_stats, | |
342 | NULL, | |
343 | NULL, | |
344 | 1); | |
bb1f6087 | 345 | } |
764df57e | 346 | |
bb1f6087 | 347 | |
f2a855d5 | 348 | int git_clone(git_repository **out, |
ea817863 BS |
349 | const char *origin_url, |
350 | const char *workdir_path, | |
ef9905c9 | 351 | git_indexer_stats *fetch_stats, |
b401bace | 352 | git_indexer_stats *checkout_stats, |
ef9905c9 | 353 | git_checkout_opts *checkout_opts) |
764df57e | 354 | { |
ea817863 | 355 | assert(out && origin_url && workdir_path); |
14741d62 | 356 | |
bf0e62a2 | 357 | return clone_internal( |
358 | out, | |
359 | origin_url, | |
360 | workdir_path, | |
361 | fetch_stats, | |
362 | checkout_stats, | |
363 | checkout_opts, | |
364 | 0); | |
764df57e | 365 | } |