Clone: local branch for remote HEAD. Now creating a local branch that tracks to the origin's HEAD branch, and setting HEAD to that.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
diff --git a/src/clone.c b/src/clone.c
index a737d97..bc26b9d 100644
--- a/src/clone.c
+++ b/src/clone.c
@@ -10,37 +10,121 @@
#include "git2/clone.h"
#include "git2/remote.h"
#include "git2/revparse.h"
+#include "git2/branch.h"
+#include "git2/config.h"
#include "common.h"
#include "remote.h"
#include "fileops.h"
+#include "refs.h"
// TODO #include "checkout.h"
GIT_BEGIN_DECL
+struct HeadInfo {
+ git_repository *repo;
+ git_oid remote_head_oid;
+ git_buf branchname;
+};
static int git_checkout_force(git_repository *repo)
{
- /* TODO */
+ /* TODO
+ * -> Line endings
+ */
+ return 0;
+}
+
+static int create_tracking_branch(struct HeadInfo *info)
+{
+ git_object *head_obj = NULL;
+ git_oid branch_oid;
+ int retcode = GIT_ERROR;
+ const char *branchname = git_buf_cstr(&info->branchname);
+
+ /* Find the target commit */
+ if (git_object_lookup(&head_obj, info->repo, &info->remote_head_oid, GIT_OBJ_ANY) < 0)
+ return GIT_ERROR;
+
+ /* Create the new branch */
+ if (!git_branch_create(&branch_oid, info->repo, branchname, head_obj, 0)) {
+ /* Set up tracking */
+ git_config *cfg;
+ if (!git_repository_config(&cfg, info->repo)) {
+ git_buf remote = GIT_BUF_INIT;
+ git_buf merge = GIT_BUF_INIT;
+ git_buf merge_target = GIT_BUF_INIT;
+ if (!git_buf_printf(&remote, "branch.%s.remote", branchname) &&
+ !git_buf_printf(&merge, "branch.%s.merge", branchname) &&
+ !git_buf_printf(&merge_target, "refs/heads/%s", branchname) &&
+ !git_config_set_string(cfg, git_buf_cstr(&remote), "origin") &&
+ !git_config_set_string(cfg, git_buf_cstr(&merge), git_buf_cstr(&merge_target))) {
+ retcode = 0;
+ }
+ git_buf_free(&remote);
+ git_buf_free(&merge);
+ git_buf_free(&merge_target);
+ git_config_free(cfg);
+ }
+ }
+
+ return retcode;
+}
+
+static int reference_matches_remote_head(const char *head_name, void *payload)
+{
+ struct HeadInfo *head_info = (struct HeadInfo *)payload;
+ git_oid oid;
+
+ /* Stop looking if we've already found a match */
+ if (git_buf_len(&head_info->branchname) > 0) return 0;
+
+ if (!git_reference_name_to_oid(&oid, head_info->repo, head_name) &&
+ !git_oid_cmp(&head_info->remote_head_oid, &oid)) {
+ /* strlen("refs/remotes/origin/") == 20 */
+ git_buf_puts(&head_info->branchname, head_name+20);
+ }
return 0;
}
static int update_head_to_remote(git_repository *repo, git_remote *remote)
{
int retcode = 0;
+ git_remote_head *remote_head;
+ struct HeadInfo head_info;
/* Get the remote's HEAD. This is always the first ref in remote->refs. */
- git_buf remote_default_branch = GIT_BUF_INIT;
- /* TODO */
-
- git_buf_free(&remote_default_branch);
+ remote_head = remote->refs.contents[0];
+ git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
+ git_buf_init(&head_info.branchname, 16);
+ head_info.repo = repo;
+
+ /* Find the branch the remote head belongs to. */
+ if (!git_reference_foreach(repo, GIT_REF_LISTALL, reference_matches_remote_head, &head_info) &&
+ git_buf_len(&head_info.branchname) > 0) {
+ if (!create_tracking_branch(&head_info)) {
+ /* Update HEAD to point to the new branch */
+ git_reference *head;
+ if (!git_reference_lookup(&head, repo, "HEAD")) {
+ git_buf target = GIT_BUF_INIT;
+ if (!git_buf_printf(&target, "refs/heads/%s", git_buf_cstr(&head_info.branchname)) &&
+ !git_reference_set_target(head, git_buf_cstr(&target))) {
+ retcode = 0;
+ }
+ git_buf_free(&target);
+ git_reference_free(head);
+ }
+ }
+ }
+ git_buf_free(&head_info.branchname);
return retcode;
}
/*
* submodules?
* filemodes?
+ * Line endings
*/