]>
Commit | Line | Data |
---|---|---|
731df570 | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
731df570 | 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 "common.h" | |
9 | #include "commit.h" | |
731df570 | 10 | #include "tag.h" |
fb910281 | 11 | #include "config.h" |
12 | #include "refspec.h" | |
bf031581 | 13 | #include "refs.h" |
4330ab26 | 14 | #include "remote.h" |
731df570 | 15 | |
326ca710 | 16 | #include "git2/branch.h" |
17 | ||
731df570 | 18 | static int retrieve_branch_reference( |
19 | git_reference **branch_reference_out, | |
20 | git_repository *repo, | |
21 | const char *branch_name, | |
22 | int is_remote) | |
23 | { | |
3cf11eef RB |
24 | git_reference *branch = NULL; |
25 | int error = 0; | |
731df570 | 26 | char *prefix; |
27 | git_buf ref_name = GIT_BUF_INIT; | |
28 | ||
731df570 | 29 | prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR; |
30 | ||
3cf11eef RB |
31 | if ((error = git_buf_joinpath(&ref_name, prefix, branch_name)) < 0) |
32 | /* OOM */; | |
33 | else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) | |
34 | giterr_set( | |
35 | GITERR_REFERENCE, "Cannot locate %s branch '%s'", | |
36 | is_remote ? "remote-tracking" : "local", branch_name); | |
731df570 | 37 | |
3cf11eef | 38 | *branch_reference_out = branch; /* will be NULL on error */ |
731df570 | 39 | |
731df570 | 40 | git_buf_free(&ref_name); |
41 | return error; | |
42 | } | |
43 | ||
bf031581 | 44 | static int not_a_local_branch(const char *reference_name) |
1c947daa | 45 | { |
bf031581 | 46 | giterr_set( |
47 | GITERR_INVALID, | |
48 | "Reference '%s' is not a local branch.", reference_name); | |
1c947daa VM |
49 | return -1; |
50 | } | |
51 | ||
731df570 | 52 | int git_branch_create( |
d00d5464 ET |
53 | git_reference **ref_out, |
54 | git_repository *repository, | |
55 | const char *branch_name, | |
56 | const git_commit *commit, | |
6bfb990d | 57 | int force) |
731df570 | 58 | { |
59b1dbcd | 59 | int is_head = 0; |
731df570 | 60 | git_reference *branch = NULL; |
59bb1126 | 61 | git_buf canonical_branch_name = GIT_BUF_INIT, |
6bfb990d | 62 | log_message = GIT_BUF_INIT; |
59bb1126 | 63 | int error = -1; |
731df570 | 64 | |
cfbe4be3 VM |
65 | assert(branch_name && commit && ref_out); |
66 | assert(git_object_owner((const git_object *)commit) == repository); | |
a07b1698 CMN |
67 | |
68 | if (force && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) { | |
69 | error = git_branch_is_head(branch); | |
70 | git_reference_free(branch); | |
71 | branch = NULL; | |
72 | ||
73 | if (error < 0) | |
59b1dbcd | 74 | goto cleanup; |
a07b1698 CMN |
75 | |
76 | is_head = error; | |
59b1dbcd | 77 | } |
731df570 | 78 | |
59b1dbcd L |
79 | if (is_head && force) { |
80 | giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is " | |
a07b1698 CMN |
81 | "the current HEAD of the repository.", branch_name); |
82 | error = -1; | |
59b1dbcd L |
83 | goto cleanup; |
84 | } | |
85 | ||
b31ebfbc BS |
86 | if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0) |
87 | goto cleanup; | |
731df570 | 88 | |
6bfb990d | 89 | if (git_buf_printf(&log_message, "Branch: created from %s", git_oid_tostr_s(git_commit_id(commit))) < 0) |
59bb1126 BS |
90 | goto cleanup; |
91 | ||
92 | error = git_reference_create(&branch, repository, | |
659cf202 | 93 | git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force, |
6bfb990d | 94 | git_buf_cstr(&log_message)); |
59bb1126 BS |
95 | |
96 | if (!error) | |
b31ebfbc | 97 | *ref_out = branch; |
731df570 | 98 | |
b31ebfbc | 99 | cleanup: |
731df570 | 100 | git_buf_free(&canonical_branch_name); |
6bfb990d | 101 | git_buf_free(&log_message); |
731df570 | 102 | return error; |
103 | } | |
104 | ||
1c947daa | 105 | int git_branch_delete(git_reference *branch) |
731df570 | 106 | { |
4ba23be1 | 107 | int is_head; |
aba70781 | 108 | git_buf config_section = GIT_BUF_INIT; |
109 | int error = -1; | |
731df570 | 110 | |
1c947daa | 111 | assert(branch); |
731df570 | 112 | |
96869a4e RB |
113 | if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) { |
114 | giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", | |
115 | git_reference_name(branch)); | |
116 | return GIT_ENOTFOUND; | |
1c947daa | 117 | } |
731df570 | 118 | |
4ba23be1 | 119 | if ((is_head = git_branch_is_head(branch)) < 0) |
120 | return is_head; | |
731df570 | 121 | |
4ba23be1 | 122 | if (is_head) { |
96869a4e RB |
123 | giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is " |
124 | "the current HEAD of the repository.", git_reference_name(branch)); | |
4ba23be1 | 125 | return -1; |
731df570 | 126 | } |
127 | ||
96869a4e RB |
128 | if (git_buf_join(&config_section, '.', "branch", |
129 | git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) | |
aba70781 | 130 | goto on_error; |
131 | ||
132 | if (git_config_rename_section( | |
96869a4e RB |
133 | git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0) |
134 | goto on_error; | |
0b98a8a4 | 135 | |
aba70781 | 136 | if (git_reference_delete(branch) < 0) |
137 | goto on_error; | |
138 | ||
31bc6c04 | 139 | if ((error = git_reflog_delete(git_reference_owner(branch), git_reference_name(branch))) < 0) { |
79863917 POL |
140 | if (error == GIT_ENOTFOUND) { |
141 | giterr_clear(); | |
142 | error = 0; | |
143 | } | |
48110f67 | 144 | goto on_error; |
79863917 | 145 | } |
48110f67 | 146 | |
aba70781 | 147 | error = 0; |
148 | ||
149 | on_error: | |
150 | git_buf_free(&config_section); | |
151 | return error; | |
731df570 | 152 | } |
153 | ||
8ec889a4 | 154 | typedef struct { |
9bd89d96 | 155 | git_reference_iterator *iter; |
8ec889a4 CMN |
156 | unsigned int flags; |
157 | } branch_iter; | |
158 | ||
a667ca82 | 159 | int git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *_iter) |
8ec889a4 CMN |
160 | { |
161 | branch_iter *iter = (branch_iter *) _iter; | |
56960b83 | 162 | git_reference *ref; |
8ec889a4 CMN |
163 | int error; |
164 | ||
165 | while ((error = git_reference_next(&ref, iter->iter)) == 0) { | |
166 | if ((iter->flags & GIT_BRANCH_LOCAL) && | |
167 | !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR)) { | |
168 | *out = ref; | |
169 | *out_type = GIT_BRANCH_LOCAL; | |
170 | ||
171 | return 0; | |
172 | } else if ((iter->flags & GIT_BRANCH_REMOTE) && | |
173 | !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) { | |
174 | *out = ref; | |
175 | *out_type = GIT_BRANCH_REMOTE; | |
176 | ||
177 | return 0; | |
178 | } else { | |
179 | git_reference_free(ref); | |
180 | } | |
181 | } | |
a8fd805e | 182 | |
8ec889a4 CMN |
183 | return error; |
184 | } | |
a8fd805e | 185 | |
8ec889a4 CMN |
186 | int git_branch_iterator_new( |
187 | git_branch_iterator **out, | |
188 | git_repository *repo, | |
a667ca82 | 189 | git_branch_t list_flags) |
8ec889a4 CMN |
190 | { |
191 | branch_iter *iter; | |
9bd89d96 | 192 | |
8ec889a4 CMN |
193 | iter = git__calloc(1, sizeof(branch_iter)); |
194 | GITERR_CHECK_ALLOC(iter); | |
56960b83 | 195 | |
8ec889a4 | 196 | iter->flags = list_flags; |
09c2f91c | 197 | |
8ec889a4 CMN |
198 | if (git_reference_iterator_new(&iter->iter, repo) < 0) { |
199 | git__free(iter); | |
200 | return -1; | |
9bd89d96 CMN |
201 | } |
202 | ||
8ec889a4 | 203 | *out = (git_branch_iterator *) iter; |
9bd89d96 | 204 | |
8ec889a4 CMN |
205 | return 0; |
206 | } | |
207 | ||
208 | void git_branch_iterator_free(git_branch_iterator *_iter) | |
209 | { | |
210 | branch_iter *iter = (branch_iter *) _iter; | |
211 | ||
9eb45fc5 BR |
212 | if (iter == NULL) |
213 | return; | |
214 | ||
8ec889a4 CMN |
215 | git_reference_iterator_free(iter->iter); |
216 | git__free(iter); | |
a8fd805e | 217 | } |
218 | ||
bf9e8cc8 | 219 | int git_branch_move( |
d00d5464 | 220 | git_reference **out, |
bf9e8cc8 | 221 | git_reference *branch, |
222 | const char *new_branch_name, | |
6bfb990d | 223 | int force) |
bf9e8cc8 | 224 | { |
383f164a | 225 | git_buf new_reference_name = GIT_BUF_INIT, |
59bb1126 BS |
226 | old_config_section = GIT_BUF_INIT, |
227 | new_config_section = GIT_BUF_INIT, | |
6bfb990d | 228 | log_message = GIT_BUF_INIT; |
bf9e8cc8 | 229 | int error; |
6a8bcfa4 | 230 | |
bf9e8cc8 | 231 | assert(branch && new_branch_name); |
232 | ||
233 | if (!git_reference_is_branch(branch)) | |
bf031581 | 234 | return not_a_local_branch(git_reference_name(branch)); |
4615f0f7 | 235 | |
59bb1126 | 236 | if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0) |
d00d5464 | 237 | goto done; |
383f164a | 238 | |
6bfb990d CMN |
239 | if ((error = git_buf_printf(&log_message, "Branch: renamed %s to %s", |
240 | git_reference_name(branch), git_buf_cstr(&new_reference_name))) < 0) | |
59bb1126 | 241 | goto done; |
59bb1126 | 242 | |
96869a4e | 243 | /* first update ref then config so failure won't trash config */ |
4e6e2ff2 | 244 | |
96869a4e | 245 | error = git_reference_rename( |
ccf6ce5c | 246 | out, branch, git_buf_cstr(&new_reference_name), force, |
6bfb990d | 247 | git_buf_cstr(&log_message)); |
96869a4e | 248 | if (error < 0) |
d00d5464 | 249 | goto done; |
3e199f42 | 250 | |
96869a4e RB |
251 | git_buf_join(&old_config_section, '.', "branch", |
252 | git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)); | |
253 | git_buf_join(&new_config_section, '.', "branch", new_branch_name); | |
254 | ||
255 | error = git_config_rename_section( | |
256 | git_reference_owner(branch), | |
257 | git_buf_cstr(&old_config_section), | |
258 | git_buf_cstr(&new_config_section)); | |
4615f0f7 | 259 | |
d00d5464 | 260 | done: |
6a8bcfa4 | 261 | git_buf_free(&new_reference_name); |
aba70781 | 262 | git_buf_free(&old_config_section); |
263 | git_buf_free(&new_config_section); | |
6bfb990d | 264 | git_buf_free(&log_message); |
6a8bcfa4 | 265 | |
6a625435 | 266 | return error; |
4615f0f7 | 267 | } |
eed378b6 | 268 | |
269 | int git_branch_lookup( | |
d00d5464 ET |
270 | git_reference **ref_out, |
271 | git_repository *repo, | |
272 | const char *branch_name, | |
273 | git_branch_t branch_type) | |
eed378b6 | 274 | { |
275 | assert(ref_out && repo && branch_name); | |
276 | ||
277 | return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE); | |
278 | } | |
fb910281 | 279 | |
3b4ba278 JG |
280 | int git_branch_name( |
281 | const char **out, | |
282 | const git_reference *ref) | |
c253056d SB |
283 | { |
284 | const char *branch_name; | |
285 | ||
286 | assert(out && ref); | |
287 | ||
288 | branch_name = ref->name; | |
289 | ||
290 | if (git_reference_is_branch(ref)) { | |
291 | branch_name += strlen(GIT_REFS_HEADS_DIR); | |
292 | } else if (git_reference_is_remote(ref)) { | |
293 | branch_name += strlen(GIT_REFS_REMOTES_DIR); | |
294 | } else { | |
295 | giterr_set(GITERR_INVALID, | |
296 | "Reference '%s' is neither a local nor a remote branch.", ref->name); | |
297 | return -1; | |
298 | } | |
299 | *out = branch_name; | |
300 | return 0; | |
301 | } | |
302 | ||
a258d8e3 | 303 | static int retrieve_upstream_configuration( |
bf031581 | 304 | const char **out, |
29c4cb09 | 305 | const git_config *config, |
bf031581 | 306 | const char *canonical_branch_name, |
307 | const char *format) | |
fb910281 | 308 | { |
fb910281 | 309 | git_buf buf = GIT_BUF_INIT; |
310 | int error; | |
311 | ||
fb910281 | 312 | if (git_buf_printf(&buf, format, |
bf031581 | 313 | canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0) |
fb910281 | 314 | return -1; |
315 | ||
316 | error = git_config_get_string(out, config, git_buf_cstr(&buf)); | |
317 | git_buf_free(&buf); | |
318 | return error; | |
319 | } | |
320 | ||
b25d87c9 CMN |
321 | int git_branch_upstream_name( |
322 | git_buf *out, | |
bf031581 | 323 | git_repository *repo, |
b25d87c9 | 324 | const char *refname) |
fb910281 | 325 | { |
326 | const char *remote_name, *merge_name; | |
327 | git_buf buf = GIT_BUF_INIT; | |
328 | int error = -1; | |
329 | git_remote *remote = NULL; | |
330 | const git_refspec *refspec; | |
ac99d86b | 331 | git_config *config; |
fb910281 | 332 | |
b25d87c9 CMN |
333 | assert(out && refname); |
334 | ||
335 | git_buf_sanitize(out); | |
fb910281 | 336 | |
b25d87c9 CMN |
337 | if (!git_reference__is_branch(refname)) |
338 | return not_a_local_branch(refname); | |
fb910281 | 339 | |
ac99d86b | 340 | if ((error = git_repository_config_snapshot(&config, repo)) < 0) |
29c4cb09 CMN |
341 | return error; |
342 | ||
a258d8e3 | 343 | if ((error = retrieve_upstream_configuration( |
29c4cb09 | 344 | &remote_name, config, refname, "branch.%s.remote")) < 0) |
bf031581 | 345 | goto cleanup; |
fb910281 | 346 | |
a258d8e3 | 347 | if ((error = retrieve_upstream_configuration( |
29c4cb09 | 348 | &merge_name, config, refname, "branch.%s.merge")) < 0) |
bf031581 | 349 | goto cleanup; |
37849a8e | 350 | |
28cbd2e2 | 351 | if (!*remote_name || !*merge_name) { |
3e199f42 | 352 | giterr_set(GITERR_REFERENCE, |
b25d87c9 | 353 | "branch '%s' does not have an upstream", refname); |
28cbd2e2 | 354 | error = GIT_ENOTFOUND; |
355 | goto cleanup; | |
356 | } | |
fb910281 | 357 | |
358 | if (strcmp(".", remote_name) != 0) { | |
209425ce | 359 | if ((error = git_remote_lookup(&remote, repo, remote_name)) < 0) |
fb910281 | 360 | goto cleanup; |
361 | ||
4330ab26 CMN |
362 | refspec = git_remote__matching_refspec(remote, merge_name); |
363 | if (!refspec) { | |
364 | error = GIT_ENOTFOUND; | |
365 | goto cleanup; | |
fb910281 | 366 | } |
367 | ||
bf522e08 | 368 | if (git_refspec_transform(&buf, refspec, merge_name) < 0) |
fb910281 | 369 | goto cleanup; |
370 | } else | |
371 | if (git_buf_sets(&buf, merge_name) < 0) | |
372 | goto cleanup; | |
373 | ||
b25d87c9 | 374 | error = git_buf_set(out, git_buf_cstr(&buf), git_buf_len(&buf)); |
fb910281 | 375 | |
376 | cleanup: | |
29c4cb09 | 377 | git_config_free(config); |
fb910281 | 378 | git_remote_free(remote); |
379 | git_buf_free(&buf); | |
380 | return error; | |
381 | } | |
0c78f685 | 382 | |
82374d98 CMN |
383 | int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname) |
384 | { | |
385 | int error; | |
386 | const char *str; | |
387 | git_config *cfg; | |
388 | ||
389 | if (!git_reference__is_branch(refname)) | |
390 | return not_a_local_branch(refname); | |
391 | ||
392 | git_buf_sanitize(buf); | |
393 | if ((error = git_repository_config_snapshot(&cfg, repo)) < 0) | |
394 | return error; | |
395 | ||
396 | if ((error = retrieve_upstream_configuration(&str, cfg, refname, "branch.%s.remote")) < 0) | |
397 | goto cleanup; | |
398 | ||
5915d700 CMN |
399 | if (!*str) { |
400 | giterr_set(GITERR_REFERENCE, "branch '%s' does not have an upstream remote", refname); | |
401 | error = GIT_ENOTFOUND; | |
402 | goto cleanup; | |
403 | } | |
404 | ||
82374d98 CMN |
405 | error = git_buf_puts(buf, str); |
406 | ||
407 | cleanup: | |
408 | git_config_free(cfg); | |
409 | return error; | |
410 | } | |
411 | ||
b25d87c9 | 412 | int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refname) |
2e3e8c88 JM |
413 | { |
414 | git_strarray remote_list = {0}; | |
97016f29 | 415 | size_t i; |
2e3e8c88 JM |
416 | git_remote *remote; |
417 | const git_refspec *fetchspec; | |
418 | int error = 0; | |
419 | char *remote_name = NULL; | |
420 | ||
b25d87c9 CMN |
421 | assert(buf && repo && refname); |
422 | ||
423 | git_buf_sanitize(buf); | |
2e3e8c88 JM |
424 | |
425 | /* Verify that this is a remote branch */ | |
b25d87c9 | 426 | if (!git_reference__is_remote(refname)) { |
c1b5e8c4 | 427 | giterr_set(GITERR_INVALID, "Reference '%s' is not a remote branch.", |
b25d87c9 | 428 | refname); |
2e3e8c88 JM |
429 | error = GIT_ERROR; |
430 | goto cleanup; | |
431 | } | |
432 | ||
433 | /* Get the remotes */ | |
434 | if ((error = git_remote_list(&remote_list, repo)) < 0) | |
435 | goto cleanup; | |
436 | ||
437 | /* Find matching remotes */ | |
438 | for (i = 0; i < remote_list.count; i++) { | |
209425ce | 439 | if ((error = git_remote_lookup(&remote, repo, remote_list.strings[i])) < 0) |
d59942c2 | 440 | continue; |
2e3e8c88 | 441 | |
b25d87c9 | 442 | fetchspec = git_remote__matching_dst_refspec(remote, refname); |
4330ab26 | 443 | if (fetchspec) { |
2e3e8c88 JM |
444 | /* If we have not already set out yet, then set |
445 | * it to the matching remote name. Otherwise | |
446 | * multiple remotes match this reference, and it | |
447 | * is ambiguous. */ | |
448 | if (!remote_name) { | |
449 | remote_name = remote_list.strings[i]; | |
450 | } else { | |
451 | git_remote_free(remote); | |
3e199f42 RB |
452 | |
453 | giterr_set(GITERR_REFERENCE, | |
b25d87c9 | 454 | "Reference '%s' is ambiguous", refname); |
2e3e8c88 JM |
455 | error = GIT_EAMBIGUOUS; |
456 | goto cleanup; | |
457 | } | |
458 | } | |
459 | ||
460 | git_remote_free(remote); | |
461 | } | |
462 | ||
463 | if (remote_name) { | |
97016f29 CMN |
464 | git_buf_clear(buf); |
465 | error = git_buf_puts(buf, remote_name); | |
2e3e8c88 | 466 | } else { |
3e199f42 | 467 | giterr_set(GITERR_REFERENCE, |
b25d87c9 | 468 | "Could not determine remote for '%s'", refname); |
2e3e8c88 | 469 | error = GIT_ENOTFOUND; |
2e3e8c88 JM |
470 | } |
471 | ||
472 | cleanup: | |
b25d87c9 CMN |
473 | if (error < 0) |
474 | git_buf_free(buf); | |
475 | ||
2e3e8c88 JM |
476 | git_strarray_free(&remote_list); |
477 | return error; | |
478 | } | |
479 | ||
a258d8e3 | 480 | int git_branch_upstream( |
3b4ba278 JG |
481 | git_reference **tracking_out, |
482 | const git_reference *branch) | |
bf031581 | 483 | { |
484 | int error; | |
485 | git_buf tracking_name = GIT_BUF_INIT; | |
486 | ||
b25d87c9 | 487 | if ((error = git_branch_upstream_name(&tracking_name, |
bf031581 | 488 | git_reference_owner(branch), git_reference_name(branch))) < 0) |
489 | return error; | |
490 | ||
491 | error = git_reference_lookup( | |
492 | tracking_out, | |
493 | git_reference_owner(branch), | |
494 | git_buf_cstr(&tracking_name)); | |
495 | ||
496 | git_buf_free(&tracking_name); | |
497 | return error; | |
498 | } | |
499 | ||
d59942c2 CMN |
500 | static int unset_upstream(git_config *config, const char *shortname) |
501 | { | |
502 | git_buf buf = GIT_BUF_INIT; | |
503 | ||
504 | if (git_buf_printf(&buf, "branch.%s.remote", shortname) < 0) | |
505 | return -1; | |
506 | ||
507 | if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0) | |
508 | goto on_error; | |
509 | ||
510 | git_buf_clear(&buf); | |
511 | if (git_buf_printf(&buf, "branch.%s.merge", shortname) < 0) | |
512 | goto on_error; | |
513 | ||
514 | if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0) | |
515 | goto on_error; | |
516 | ||
517 | git_buf_free(&buf); | |
518 | return 0; | |
519 | ||
520 | on_error: | |
521 | git_buf_free(&buf); | |
522 | return -1; | |
523 | } | |
524 | ||
525 | int git_branch_set_upstream(git_reference *branch, const char *upstream_name) | |
526 | { | |
527 | git_buf key = GIT_BUF_INIT, value = GIT_BUF_INIT; | |
528 | git_reference *upstream; | |
529 | git_repository *repo; | |
530 | git_remote *remote = NULL; | |
531 | git_config *config; | |
532 | const char *name, *shortname; | |
533 | int local; | |
534 | const git_refspec *fetchspec; | |
535 | ||
536 | name = git_reference_name(branch); | |
537 | if (!git_reference__is_branch(name)) | |
538 | return not_a_local_branch(name); | |
539 | ||
540 | if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0) | |
541 | return -1; | |
542 | ||
543 | shortname = name + strlen(GIT_REFS_HEADS_DIR); | |
544 | ||
545 | if (upstream_name == NULL) | |
546 | return unset_upstream(config, shortname); | |
547 | ||
548 | repo = git_reference_owner(branch); | |
549 | ||
550 | /* First we need to figure out whether it's a branch or remote-tracking */ | |
551 | if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_LOCAL) == 0) | |
552 | local = 1; | |
553 | else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0) | |
554 | local = 0; | |
3e199f42 RB |
555 | else { |
556 | giterr_set(GITERR_REFERENCE, | |
557 | "Cannot set upstream for branch '%s'", shortname); | |
d59942c2 | 558 | return GIT_ENOTFOUND; |
3e199f42 | 559 | } |
d59942c2 CMN |
560 | |
561 | /* | |
562 | * If it's local, the remote is "." and the branch name is | |
563 | * simply the refname. Otherwise we need to figure out what | |
564 | * the remote-tracking branch's name on the remote is and use | |
565 | * that. | |
566 | */ | |
567 | if (local) | |
568 | git_buf_puts(&value, "."); | |
569 | else | |
b25d87c9 | 570 | git_branch_remote_name(&value, repo, git_reference_name(upstream)); |
d59942c2 CMN |
571 | |
572 | if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0) | |
573 | goto on_error; | |
574 | ||
575 | if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0) | |
576 | goto on_error; | |
577 | ||
578 | if (local) { | |
3d42e9a3 NV |
579 | git_buf_clear(&value); |
580 | if (git_buf_puts(&value, git_reference_name(upstream)) < 0) | |
d59942c2 CMN |
581 | goto on_error; |
582 | } else { | |
583 | /* Get the remoe-tracking branch's refname in its repo */ | |
209425ce | 584 | if (git_remote_lookup(&remote, repo, git_buf_cstr(&value)) < 0) |
d59942c2 CMN |
585 | goto on_error; |
586 | ||
4330ab26 | 587 | fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream)); |
d59942c2 | 588 | git_buf_clear(&value); |
bf522e08 | 589 | if (!fetchspec || git_refspec_rtransform(&value, fetchspec, git_reference_name(upstream)) < 0) |
d59942c2 CMN |
590 | goto on_error; |
591 | ||
592 | git_remote_free(remote); | |
593 | remote = NULL; | |
594 | } | |
595 | ||
596 | git_buf_clear(&key); | |
597 | if (git_buf_printf(&key, "branch.%s.merge", shortname) < 0) | |
598 | goto on_error; | |
599 | ||
600 | if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0) | |
601 | goto on_error; | |
602 | ||
603 | git_reference_free(upstream); | |
604 | git_buf_free(&key); | |
605 | git_buf_free(&value); | |
606 | ||
607 | return 0; | |
608 | ||
609 | on_error: | |
610 | git_reference_free(upstream); | |
611 | git_buf_free(&key); | |
612 | git_buf_free(&value); | |
613 | git_remote_free(remote); | |
614 | ||
615 | return -1; | |
616 | } | |
617 | ||
0c78f685 | 618 | int git_branch_is_head( |
853b1407 | 619 | const git_reference *branch) |
0c78f685 | 620 | { |
621 | git_reference *head; | |
622 | bool is_same = false; | |
0532e7bb | 623 | int error; |
0c78f685 | 624 | |
625 | assert(branch); | |
626 | ||
627 | if (!git_reference_is_branch(branch)) | |
628 | return false; | |
629 | ||
0532e7bb | 630 | error = git_repository_head(&head, git_reference_owner(branch)); |
631 | ||
605da51a | 632 | if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND) |
0532e7bb | 633 | return false; |
634 | ||
635 | if (error < 0) | |
0c78f685 | 636 | return -1; |
637 | ||
638 | is_same = strcmp( | |
639 | git_reference_name(branch), | |
640 | git_reference_name(head)) == 0; | |
641 | ||
642 | git_reference_free(head); | |
643 | ||
644 | return is_same; | |
645 | } |