]> git.proxmox.com Git - libgit2.git/blame - src/clone.c
Fix clone sample
[libgit2.git] / src / clone.c
CommitLineData
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 25static int create_branch(
7eca3c56 26 git_reference **branch,
27 git_repository *repo,
28 const git_oid *target,
29 const char *name)
4fbc899a 30{
cfbe4be3 31 git_commit *head_obj = NULL;
8a155a04 32 git_reference *branch_ref;
bf0e62a2 33 int error;
ea817863
BS
34
35 /* Find the target commit */
cfbe4be3 36 if ((error = git_commit_lookup(&head_obj, repo, target)) < 0)
bf0e62a2 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 41
cfbe4be3 42 git_commit_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
52static 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
79cleanup:
80 git_buf_free(&remote_key);
81 git_buf_free(&merge_key);
82 return error;
83}
84
85static 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 103struct 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 110static 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
2508cc66 125 if (git_reference_name_to_id(
d280c71b 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 151static int update_head_to_new_branch(
152 git_repository *repo,
153 const git_oid *target,
154 const char *name)
af58ec9e 155{
aa4437f6 156 git_reference *tracking_branch = NULL;
7eca3c56 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
41fb1ca0
PK
173static int get_head_callback(git_remote_head *head, void *payload)
174{
175 git_remote_head **destination = (git_remote_head **)payload;
176
177 /* Save the first entry, and terminate the enumeration */
178 *destination = head;
179 return 1;
180}
181
8340dd5d
BS
182static int update_head_to_remote(git_repository *repo, git_remote *remote)
183{
d280c71b 184 int retcode = -1;
ea817863 185 git_remote_head *remote_head;
70edc1b0 186 struct head_info head_info;
d280c71b 187 git_buf remote_master_name = GIT_BUF_INIT;
ea817863 188
bf0e62a2 189 /* Did we just clone an empty repository? */
190 if (remote->refs.length == 0) {
191 return setup_tracking_config(
192 repo,
193 "master",
194 GIT_REMOTE_ORIGIN,
195 GIT_REFS_HEADS_MASTER_FILE);
196 }
197
ea817863 198 /* Get the remote's HEAD. This is always the first ref in remote->refs. */
41fb1ca0
PK
199 remote_head = NULL;
200
201 if (!remote->transport->ls(remote->transport, get_head_callback, &remote_head))
202 return -1;
203
204 assert(remote_head);
205
ea817863
BS
206 git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
207 git_buf_init(&head_info.branchname, 16);
208 head_info.repo = repo;
d280c71b 209 head_info.refspec = git_remote_fetchspec(remote);
210
211 /* Determine the remote tracking reference name from the local master */
212 if (git_refspec_transform_r(
213 &remote_master_name,
214 head_info.refspec,
215 GIT_REFS_HEADS_MASTER_FILE) < 0)
216 return -1;
217
218 /* Check to see if the remote HEAD points to the remote master */
219 if (reference_matches_remote_head(git_buf_cstr(&remote_master_name), &head_info) < 0)
220 goto cleanup;
221
222 if (git_buf_len(&head_info.branchname) > 0) {
223 retcode = update_head_to_new_branch(
224 repo,
225 &head_info.remote_head_oid,
226 git_buf_cstr(&head_info.branchname));
227
228 goto cleanup;
ea817863 229 }
d280c71b 230
ea817863 231 /* Not master. Check all the other refs. */
d280c71b 232 if (git_reference_foreach(
233 repo,
234 GIT_REF_LISTALL,
235 reference_matches_remote_head,
236 &head_info) < 0)
237 goto cleanup;
238
239 if (git_buf_len(&head_info.branchname) > 0) {
240 retcode = update_head_to_new_branch(
241 repo,
242 &head_info.remote_head_oid,
243 git_buf_cstr(&head_info.branchname));
244
245 goto cleanup;
246 } else {
247 /* TODO: What should we do if nothing has been found?
248 */
ea817863
BS
249 }
250
d280c71b 251cleanup:
252 git_buf_free(&remote_master_name);
ea817863
BS
253 git_buf_free(&head_info.branchname);
254 return retcode;
bb1f6087
BS
255}
256
764df57e
BS
257/*
258 * submodules?
764df57e
BS
259 */
260
b412d563
BS
261static int create_and_configure_origin(
262 git_remote **out,
263 git_repository *repo,
264 const char *url,
265 const git_clone_options *options)
266{
267 int error;
00998a12 268 git_remote *origin = NULL;
b412d563
BS
269
270 if ((error = git_remote_add(&origin, repo, options->remote_name, url)) < 0)
271 goto on_error;
272
273 git_remote_set_cred_acquire_cb(origin, options->cred_acquire_cb,
274 options->cred_acquire_payload);
275 git_remote_set_autotag(origin, options->remote_autotag);
276 /*
277 * Don't write FETCH_HEAD, we'll check out the remote tracking
278 * branch ourselves based on the server's default.
279 */
280 git_remote_set_update_fetchhead(origin, 0);
281
282 if (options->remote_callbacks &&
283 (error = git_remote_set_callbacks(origin, options->remote_callbacks)) < 0)
284 goto on_error;
285
286 if (options->fetch_spec &&
287 (error = git_remote_set_fetchspec(origin, options->fetch_spec)) < 0)
288 goto on_error;
289
290 if (options->push_spec &&
291 (error = git_remote_set_pushspec(origin, options->push_spec)) < 0)
292 goto on_error;
293
294 if (options->pushurl &&
295 (error = git_remote_set_pushurl(origin, options->pushurl)) < 0)
296 goto on_error;
297
621b50e4
BS
298 if ((error = git_remote_save(origin)) < 0)
299 goto on_error;
300
b412d563
BS
301 *out = origin;
302 return 0;
303
304on_error:
00998a12 305 git_remote_free(origin);
b412d563
BS
306 return error;
307}
bb1f6087
BS
308
309
aa1e8674
BS
310static int setup_remotes_and_fetch(
311 git_repository *repo,
b412d563
BS
312 const char *url,
313 const git_clone_options *options)
764df57e 314{
ea817863 315 int retcode = GIT_ERROR;
b412d563 316 git_remote *origin;
ea817863 317
b412d563
BS
318 /* Construct an origin remote */
319 if (!create_and_configure_origin(&origin, repo, url, options)) {
b0f6e45d
ET
320 git_remote_set_update_fetchhead(origin, 0);
321
ea817863 322 /* Connect and download everything */
df705148 323 if (!git_remote_connect(origin, GIT_DIRECTION_FETCH)) {
b412d563
BS
324 if (!git_remote_download(origin, options->fetch_progress_cb,
325 options->fetch_progress_payload)) {
ea817863 326 /* Create "origin/foo" branches for all remote branches */
7affe23d 327 if (!git_remote_update_tips(origin)) {
ea817863
BS
328 /* Point HEAD to the same ref as the remote's head */
329 if (!update_head_to_remote(repo, origin)) {
330 retcode = 0;
331 }
332 }
333 }
334 git_remote_disconnect(origin);
335 }
ea817863
BS
336 }
337
338 return retcode;
764df57e
BS
339}
340
acdd3d95 341
acdd3d95
BS
342static bool path_is_okay(const char *path)
343{
ea817863
BS
344 /* The path must either not exist, or be an empty directory */
345 if (!git_path_exists(path)) return true;
d024419f 346 if (!git_path_is_empty_dir(path)) {
ea817863
BS
347 giterr_set(GITERR_INVALID,
348 "'%s' exists and is not an empty directory", path);
349 return false;
350 }
d024419f 351 return true;
acdd3d95
BS
352}
353
4d968f13 354static bool should_checkout(
355 git_repository *repo,
356 bool is_bare,
357 git_checkout_opts *opts)
358{
359 if (is_bare)
360 return false;
361
362 if (!opts)
363 return false;
364
365 return !git_repository_head_orphan(repo);
366}
acdd3d95 367
b412d563
BS
368static void normalize_options(git_clone_options *dst, const git_clone_options *src)
369{
370 git_clone_options default_options = GIT_CLONE_OPTIONS_INIT;
371 if (!src) src = &default_options;
372
373 *dst = *src;
374
375 /* Provide defaults for null pointers */
376 if (!dst->remote_name) dst->remote_name = "origin";
377}
378
379int git_clone(
bf0e62a2 380 git_repository **out,
b412d563
BS
381 const char *url,
382 const char *local_path,
383 const git_clone_options *options)
764df57e 384{
ea817863
BS
385 int retcode = GIT_ERROR;
386 git_repository *repo = NULL;
b412d563
BS
387 git_clone_options normOptions;
388
389 assert(out && url && local_path);
ea817863 390
b412d563
BS
391 normalize_options(&normOptions, options);
392 GITERR_CHECK_VERSION(&normOptions, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
393
394 if (!path_is_okay(local_path)) {
ea817863
BS
395 return GIT_ERROR;
396 }
397
b412d563
BS
398 if (!(retcode = git_repository_init(&repo, local_path, normOptions.bare))) {
399 if ((retcode = setup_remotes_and_fetch(repo, url, &normOptions)) < 0) {
ea817863
BS
400 /* Failed to fetch; clean up */
401 git_repository_free(repo);
b412d563 402 git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES);
ea817863
BS
403 } else {
404 *out = repo;
405 retcode = 0;
406 }
407 }
408
b412d563
BS
409 if (!retcode && should_checkout(repo, normOptions.bare, normOptions.checkout_opts))
410 retcode = git_checkout_head(*out, normOptions.checkout_opts);
bf0e62a2 411
ea817863 412 return retcode;
764df57e 413}