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