]> git.proxmox.com Git - libgit2.git/blame - src/clone.c
Merge pull request #949 from nulltoken/topic/deploy_repository_set_head
[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{
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
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
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 151static 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
173static 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 236cleanup:
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 248static 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
281static 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 294static 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 330int 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 348int 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}