int notes_merge_commit(struct notes_merge_options *o, struct notes_tree *partial_tree, struct commit *partial_commit, unsigned char *result_sha1) { /* * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all * found notes to 'partial_tree'. Write the updates notes tree to * the DB, and commit the resulting tree object while reusing the * commit message and parents from 'partial_commit'. * Finally store the new commit object SHA1 into 'result_sha1'. */ struct dir_struct dir; char *path = xstrdup(git_path(NOTES_MERGE_WORKTREE "/")); int path_len = strlen(path), i; const char *msg = strstr(partial_commit->buffer, "\n\n"); OUTPUT(o, 3, "Committing notes in notes merge worktree at %.*s", path_len - 1, path); if (!msg || msg[2] == '\0') die("partial notes commit has empty message"); msg += 2; memset(&dir, 0, sizeof(dir)); read_directory(&dir, path, path_len, NULL); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; struct stat st; const char *relpath = ent->name + path_len; unsigned char obj_sha1[20], blob_sha1[20]; if (ent->len - path_len != 40 || get_sha1_hex(relpath, obj_sha1)) { OUTPUT(o, 3, "Skipping non-SHA1 entry '%s'", ent->name); continue; } /* write file as blob, and add to partial_tree */ if (stat(ent->name, &st)) die_errno("Failed to stat '%s'", ent->name); if (index_path(blob_sha1, ent->name, &st, HASH_WRITE_OBJECT)) die("Failed to write blob object from '%s'", ent->name); if (add_note(partial_tree, obj_sha1, blob_sha1, NULL)) die("Failed to add resolved note '%s' to notes tree", ent->name); OUTPUT(o, 4, "Added resolved note for object %s: %s", sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1)); } create_notes_commit(partial_tree, partial_commit->parents, msg, result_sha1); OUTPUT(o, 4, "Finalized notes merge commit: %s", sha1_to_hex(result_sha1)); free(path); return 0; }
void commit_notes(struct notes_tree *t, const char *msg) { struct strbuf buf = STRBUF_INIT; unsigned char commit_sha1[20]; if (!t) t = &default_notes_tree; if (!t->initialized || !t->ref || !*t->ref) die(_("Cannot commit uninitialized/unreferenced notes tree")); if (!t->dirty) return; /* don't have to commit an unchanged tree */ /* Prepare commit message and reflog message */ strbuf_addstr(&buf, msg); strbuf_complete_line(&buf); create_notes_commit(t, NULL, buf.buf, buf.len, commit_sha1); strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */ update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, UPDATE_REFS_DIE_ON_ERR); strbuf_release(&buf); }
void commit_notes(struct notes_tree *t, const char *msg) { struct strbuf buf = STRBUF_INIT; unsigned char commit_sha1[20]; if (!t) t = &default_notes_tree; if (!t->initialized || !t->ref || !*t->ref) die("Cannot commit uninitialized/unreferenced notes tree"); if (!t->dirty) return; /* don't have to commit an unchanged tree */ /* Prepare commit message and reflog message */ strbuf_addstr(&buf, "notes: "); /* commit message starts at index 7 */ strbuf_addstr(&buf, msg); if (buf.buf[buf.len - 1] != '\n') strbuf_addch(&buf, '\n'); /* Make sure msg ends with newline */ create_notes_commit(t, NULL, buf.buf + 7, commit_sha1); update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR); strbuf_release(&buf); }
int notes_merge(struct notes_merge_options *o, struct notes_tree *local_tree, unsigned char *result_sha1) { unsigned char local_sha1[20], remote_sha1[20]; struct commit *local, *remote; struct commit_list *bases = NULL; const unsigned char *base_sha1, *base_tree_sha1; int result = 0; assert(o->local_ref && o->remote_ref); assert(!strcmp(o->local_ref, local_tree->ref)); hashclr(result_sha1); trace_printf("notes_merge(o->local_ref = %s, o->remote_ref = %s)\n", o->local_ref, o->remote_ref); /* Dereference o->local_ref into local_sha1 */ if (read_ref_full(o->local_ref, local_sha1, 0, NULL)) die("Failed to resolve local notes ref '%s'", o->local_ref); else if (!check_refname_format(o->local_ref, 0) && is_null_sha1(local_sha1)) local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */ else if (!(local = lookup_commit_reference(local_sha1))) die("Could not parse local commit %s (%s)", sha1_to_hex(local_sha1), o->local_ref); trace_printf("\tlocal commit: %.7s\n", sha1_to_hex(local_sha1)); /* Dereference o->remote_ref into remote_sha1 */ if (get_sha1(o->remote_ref, remote_sha1)) { /* * Failed to get remote_sha1. If o->remote_ref looks like an * unborn ref, perform the merge using an empty notes tree. */ if (!check_refname_format(o->remote_ref, 0)) { hashclr(remote_sha1); remote = NULL; } else { die("Failed to resolve remote notes ref '%s'", o->remote_ref); } } else if (!(remote = lookup_commit_reference(remote_sha1))) { die("Could not parse remote commit %s (%s)", sha1_to_hex(remote_sha1), o->remote_ref); } trace_printf("\tremote commit: %.7s\n", sha1_to_hex(remote_sha1)); if (!local && !remote) die("Cannot merge empty notes ref (%s) into empty notes ref " "(%s)", o->remote_ref, o->local_ref); if (!local) { /* result == remote commit */ hashcpy(result_sha1, remote_sha1); goto found_result; } if (!remote) { /* result == local commit */ hashcpy(result_sha1, local_sha1); goto found_result; } assert(local && remote); /* Find merge bases */ bases = get_merge_bases(local, remote, 1); if (!bases) { base_sha1 = null_sha1; base_tree_sha1 = EMPTY_TREE_SHA1_BIN; if (o->verbosity >= 4) printf("No merge base found; doing history-less merge\n"); } else if (!bases->next) { base_sha1 = bases->item->object.sha1; base_tree_sha1 = bases->item->tree->object.sha1; if (o->verbosity >= 4) printf("One merge base found (%.7s)\n", sha1_to_hex(base_sha1)); } else { /* TODO: How to handle multiple merge-bases? */ base_sha1 = bases->item->object.sha1; base_tree_sha1 = bases->item->tree->object.sha1; if (o->verbosity >= 3) printf("Multiple merge bases found. Using the first " "(%.7s)\n", sha1_to_hex(base_sha1)); } if (o->verbosity >= 4) printf("Merging remote commit %.7s into local commit %.7s with " "merge-base %.7s\n", sha1_to_hex(remote->object.sha1), sha1_to_hex(local->object.sha1), sha1_to_hex(base_sha1)); if (!hashcmp(remote->object.sha1, base_sha1)) { /* Already merged; result == local commit */ if (o->verbosity >= 2) printf("Already up-to-date!\n"); hashcpy(result_sha1, local->object.sha1); goto found_result; } if (!hashcmp(local->object.sha1, base_sha1)) { /* Fast-forward; result == remote commit */ if (o->verbosity >= 2) printf("Fast-forward\n"); hashcpy(result_sha1, remote->object.sha1); goto found_result; } result = merge_from_diffs(o, base_tree_sha1, local->tree->object.sha1, remote->tree->object.sha1, local_tree); if (result != 0) { /* non-trivial merge (with or without conflicts) */ /* Commit (partial) result */ struct commit_list *parents = NULL; commit_list_insert(remote, &parents); /* LIFO order */ commit_list_insert(local, &parents); create_notes_commit(local_tree, parents, o->commit_msg.buf, result_sha1); } found_result: free_commit_list(bases); strbuf_release(&(o->commit_msg)); trace_printf("notes_merge(): result = %i, result_sha1 = %.7s\n", result, sha1_to_hex(result_sha1)); return result; }
int notes_merge_commit(struct notes_merge_options *o, struct notes_tree *partial_tree, struct commit *partial_commit, struct object_id *result_oid) { /* * Iterate through files in .git/NOTES_MERGE_WORKTREE and add all * found notes to 'partial_tree'. Write the updated notes tree to * the DB, and commit the resulting tree object while reusing the * commit message and parents from 'partial_commit'. * Finally store the new commit object OID into 'result_oid'. */ DIR *dir; struct dirent *e; struct strbuf path = STRBUF_INIT; const char *buffer = get_commit_buffer(partial_commit, NULL); const char *msg = strstr(buffer, "\n\n"); int baselen; git_path_buf(&path, NOTES_MERGE_WORKTREE); if (o->verbosity >= 3) printf("Committing notes in notes merge worktree at %s\n", path.buf); if (!msg || msg[2] == '\0') die("partial notes commit has empty message"); msg += 2; dir = opendir(path.buf); if (!dir) die_errno("could not open %s", path.buf); strbuf_addch(&path, '/'); baselen = path.len; while ((e = readdir(dir)) != NULL) { struct stat st; struct object_id obj_oid, blob_oid; if (is_dot_or_dotdot(e->d_name)) continue; if (get_oid_hex(e->d_name, &obj_oid)) { if (o->verbosity >= 3) printf("Skipping non-SHA1 entry '%s%s'\n", path.buf, e->d_name); continue; } strbuf_addstr(&path, e->d_name); /* write file as blob, and add to partial_tree */ if (stat(path.buf, &st)) die_errno("Failed to stat '%s'", path.buf); if (index_path(&blob_oid, path.buf, &st, HASH_WRITE_OBJECT)) die("Failed to write blob object from '%s'", path.buf); if (add_note(partial_tree, &obj_oid, &blob_oid, NULL)) die("Failed to add resolved note '%s' to notes tree", path.buf); if (o->verbosity >= 4) printf("Added resolved note for object %s: %s\n", oid_to_hex(&obj_oid), oid_to_hex(&blob_oid)); strbuf_setlen(&path, baselen); } create_notes_commit(partial_tree, partial_commit->parents, msg, strlen(msg), result_oid->hash); unuse_commit_buffer(partial_commit, buffer); if (o->verbosity >= 4) printf("Finalized notes merge commit: %s\n", oid_to_hex(result_oid)); strbuf_release(&path); closedir(dir); return 0; }
int notes_merge(struct notes_merge_options *o, struct notes_tree *local_tree, struct object_id *result_oid) { struct object_id local_oid, remote_oid; struct commit *local, *remote; struct commit_list *bases = NULL; const struct object_id *base_oid, *base_tree_oid; int result = 0; assert(o->local_ref && o->remote_ref); assert(!strcmp(o->local_ref, local_tree->ref)); oidclr(result_oid); trace_printf("notes_merge(o->local_ref = %s, o->remote_ref = %s)\n", o->local_ref, o->remote_ref); /* Dereference o->local_ref into local_sha1 */ if (read_ref_full(o->local_ref, 0, &local_oid, NULL)) die("Failed to resolve local notes ref '%s'", o->local_ref); else if (!check_refname_format(o->local_ref, 0) && is_null_oid(&local_oid)) local = NULL; /* local_oid == null_oid indicates unborn ref */ else if (!(local = lookup_commit_reference(&local_oid))) die("Could not parse local commit %s (%s)", oid_to_hex(&local_oid), o->local_ref); trace_printf("\tlocal commit: %.7s\n", oid_to_hex(&local_oid)); /* Dereference o->remote_ref into remote_oid */ if (get_oid(o->remote_ref, &remote_oid)) { /* * Failed to get remote_oid. If o->remote_ref looks like an * unborn ref, perform the merge using an empty notes tree. */ if (!check_refname_format(o->remote_ref, 0)) { oidclr(&remote_oid); remote = NULL; } else { die("Failed to resolve remote notes ref '%s'", o->remote_ref); } } else if (!(remote = lookup_commit_reference(&remote_oid))) { die("Could not parse remote commit %s (%s)", oid_to_hex(&remote_oid), o->remote_ref); } trace_printf("\tremote commit: %.7s\n", oid_to_hex(&remote_oid)); if (!local && !remote) die("Cannot merge empty notes ref (%s) into empty notes ref " "(%s)", o->remote_ref, o->local_ref); if (!local) { /* result == remote commit */ oidcpy(result_oid, &remote_oid); goto found_result; } if (!remote) { /* result == local commit */ oidcpy(result_oid, &local_oid); goto found_result; } assert(local && remote); /* Find merge bases */ bases = get_merge_bases(local, remote); if (!bases) { base_oid = &null_oid; base_tree_oid = the_hash_algo->empty_tree; if (o->verbosity >= 4) printf("No merge base found; doing history-less merge\n"); } else if (!bases->next) { base_oid = &bases->item->object.oid; base_tree_oid = &bases->item->tree->object.oid; if (o->verbosity >= 4) printf("One merge base found (%.7s)\n", oid_to_hex(base_oid)); } else { /* TODO: How to handle multiple merge-bases? */ base_oid = &bases->item->object.oid; base_tree_oid = &bases->item->tree->object.oid; if (o->verbosity >= 3) printf("Multiple merge bases found. Using the first " "(%.7s)\n", oid_to_hex(base_oid)); } if (o->verbosity >= 4) printf("Merging remote commit %.7s into local commit %.7s with " "merge-base %.7s\n", oid_to_hex(&remote->object.oid), oid_to_hex(&local->object.oid), oid_to_hex(base_oid)); if (!oidcmp(&remote->object.oid, base_oid)) { /* Already merged; result == local commit */ if (o->verbosity >= 2) printf("Already up to date!\n"); oidcpy(result_oid, &local->object.oid); goto found_result; } if (!oidcmp(&local->object.oid, base_oid)) { /* Fast-forward; result == remote commit */ if (o->verbosity >= 2) printf("Fast-forward\n"); oidcpy(result_oid, &remote->object.oid); goto found_result; } result = merge_from_diffs(o, base_tree_oid, &local->tree->object.oid, &remote->tree->object.oid, local_tree); if (result != 0) { /* non-trivial merge (with or without conflicts) */ /* Commit (partial) result */ struct commit_list *parents = NULL; commit_list_insert(remote, &parents); /* LIFO order */ commit_list_insert(local, &parents); create_notes_commit(local_tree, parents, o->commit_msg.buf, o->commit_msg.len, result_oid->hash); } found_result: free_commit_list(bases); strbuf_release(&(o->commit_msg)); trace_printf("notes_merge(): result = %i, result_oid = %.7s\n", result, oid_to_hex(result_oid)); return result; }