static char *replace_encoding_header(char *buf, const char *encoding) { struct strbuf tmp = STRBUF_INIT; size_t start, len; char *cp = buf; /* guess if there is an encoding header before a \n\n */ while (strncmp(cp, "encoding ", strlen("encoding "))) { cp = strchr(cp, '\n'); if (!cp || *++cp == '\n') return buf; } start = cp - buf; cp = strchr(cp, '\n'); if (!cp) return buf; /* should not happen but be defensive */ len = cp + 1 - (buf + start); strbuf_attach(&tmp, buf, strlen(buf), strlen(buf) + 1); if (is_encoding_utf8(encoding)) { /* we have re-coded to UTF-8; drop the header */ strbuf_remove(&tmp, start, len); } else { /* just replaces XXXX in 'encoding XXXX\n' */ strbuf_splice(&tmp, start + strlen("encoding "), len - strlen("encoding \n"), encoding, strlen(encoding)); } return strbuf_detach(&tmp, NULL); }
static int handle_cache(const char *path, unsigned char *sha1, const char *output) { mmfile_t mmfile[3]; mmbuffer_t result = {NULL, 0}; struct cache_entry *ce; int pos, len, i, hunk_no; struct rerere_io_mem io; int marker_size = ll_merge_marker_size(path); /* * Reproduce the conflicted merge in-core */ len = strlen(path); pos = cache_name_pos(path, len); if (0 <= pos) return -1; pos = -pos - 1; for (i = 0; i < 3; i++) { enum object_type type; unsigned long size; mmfile[i].size = 0; mmfile[i].ptr = NULL; if (active_nr <= pos) break; ce = active_cache[pos++]; if (ce_namelen(ce) != len || memcmp(ce->name, path, len) || ce_stage(ce) != i + 1) break; mmfile[i].ptr = read_sha1_file(ce->sha1, &type, &size); mmfile[i].size = size; } for (i = 0; i < 3; i++) { if (!mmfile[i].ptr && !mmfile[i].size) mmfile[i].ptr = xstrdup(""); } ll_merge(&result, path, &mmfile[0], &mmfile[1], "ours", &mmfile[2], "theirs", 0); for (i = 0; i < 3; i++) free(mmfile[i].ptr); memset(&io, 0, sizeof(io)); io.io.getline = rerere_mem_getline; if (output) io.io.output = fopen(output, "w"); else io.io.output = NULL; strbuf_init(&io.input, 0); strbuf_attach(&io.input, result.ptr, result.size, result.size); hunk_no = handle_path(sha1, (struct rerere_io *)&io, marker_size); strbuf_release(&io.input); if (io.io.output) fclose(io.io.output); return hunk_no; }
int strbuf_reencode(struct strbuf *sb, const char *from, const char *to) { char *out; int len; if (same_encoding(from, to)) return 0; out = reencode_string_len(sb->buf, sb->len, to, from, &len); if (!out) return -1; strbuf_attach(sb, out, len, len); return 0; }
void format_commit_message(const struct commit *commit, const char *format, struct strbuf *sb, const struct pretty_print_context *pretty_ctx) { struct format_commit_context context; const char *output_enc = pretty_ctx->output_encoding; const char *utf8 = "UTF-8"; memset(&context, 0, sizeof(context)); context.commit = commit; context.pretty_ctx = pretty_ctx; context.wrap_start = sb->len; /* * convert a commit message to UTF-8 first * as far as 'format_commit_item' assumes it in UTF-8 */ context.message = logmsg_reencode(commit, &context.commit_encoding, utf8); strbuf_expand(sb, format, format_commit_item, &context); rewrap_message_tail(sb, &context, 0, 0, 0); /* then convert a commit message to an actual output encoding */ if (output_enc) { if (same_encoding(utf8, output_enc)) output_enc = NULL; } else { if (context.commit_encoding && !same_encoding(context.commit_encoding, utf8)) output_enc = context.commit_encoding; } if (output_enc) { int outsz; char *out = reencode_string_len(sb->buf, sb->len, output_enc, utf8, &outsz); if (out) strbuf_attach(sb, out, outsz, outsz + 1); } free(context.commit_encoding); unuse_commit_buffer(commit, context.message); free(context.signature_check.gpg_output); free(context.signature_check.signer); }
int git2_creat_force(const char *path, int mode) { if (git2_mkdir_2file(path) < GIT_OK) return GIT_ERROR; if (!git2_isdir(path)) { struct strbuf pathbuf = STRBUF_INIT; char * buf_path = xmalloc(strlen(path)*sizeof(char)); memcpy(buf_path, path, strlen(path)*sizeof(char)); strbuf_attach(&pathbuf, (void *)buf_path, strlen(path)*sizeof(char), strlen(path)*sizeof(char)); if (remove_dir_recursively(&pathbuf) != 0) return GIT_ERROR; } return git2_creat(path, mode); }
static void convert_to_utf8(struct strbuf *line, const char *charset) { char *out; if (!charset || !*charset) { charset = guess_charset(line, metainfo_charset); if (!charset) return; } if (!strcasecmp(metainfo_charset, charset)) return; out = reencode_string(line->buf, metainfo_charset, charset); if (!out) die("cannot convert from %s to %s", charset, metainfo_charset); strbuf_attach(line, out, strlen(out), strlen(out)); }
static int convert_to_utf8(struct mailinfo *mi, struct strbuf *line, const char *charset) { char *out; if (!mi->metainfo_charset || !charset || !*charset) return 0; if (same_encoding(mi->metainfo_charset, charset)) return 0; out = reencode_string(line->buf, mi->metainfo_charset, charset); if (!out) { mi->input_error = -1; return error("cannot convert from %s to %s", charset, mi->metainfo_charset); } strbuf_attach(line, out, strlen(out), strlen(out)); return 0; }
static void *sha1_file_to_archive(const char *path, const unsigned char *sha1, unsigned int mode, enum object_type *type, unsigned long *sizep, const struct commit *commit) { void *buffer; buffer = read_sha1_file(sha1, type, sizep); if (buffer && S_ISREG(mode)) { struct strbuf buf = STRBUF_INIT; size_t size = 0; strbuf_attach(&buf, buffer, *sizep, *sizep + 1); convert_to_working_tree(path, buf.buf, buf.len, &buf); if (commit) format_subst(commit, buf.buf, buf.len, &buf); buffer = strbuf_detach(&buf, &size); *sizep = size; } return buffer; }
static int string_list_add_note_lines(struct string_list *sort_uniq_list, const unsigned char *sha1) { char *data; unsigned long len; enum object_type t; struct strbuf buf = STRBUF_INIT; struct strbuf **lines = NULL; int i, list_index; if (is_null_sha1(sha1)) return 0; /* read_sha1_file NUL-terminates */ data = read_sha1_file(sha1, &t, &len); if (t != OBJ_BLOB || !data || !len) { free(data); return t != OBJ_BLOB || !data; } strbuf_attach(&buf, data, len, len + 1); lines = strbuf_split(&buf, '\n'); for (i = 0; lines[i]; i++) { if (lines[i]->buf[lines[i]->len - 1] == '\n') strbuf_setlen(lines[i], lines[i]->len - 1); if (!lines[i]->len) continue; /* skip empty lines */ list_index = string_list_find_insert_index(sort_uniq_list, lines[i]->buf, 0); if (list_index < 0) continue; /* skip duplicate lines */ string_list_insert_at_index(sort_uniq_list, list_index, lines[i]->buf); } strbuf_list_free(lines); strbuf_release(&buf); return 0; }
int notes_cache_write(struct notes_cache *c) { unsigned char tree_sha1[20]; unsigned char commit_sha1[20]; struct strbuf msg = STRBUF_INIT; if (!c || !c->tree.initialized || !c->tree.ref || !*c->tree.ref) return -1; if (!c->tree.dirty) return 0; if (write_notes_tree(&c->tree, tree_sha1)) return -1; strbuf_attach(&msg, c->validity, strlen(c->validity), strlen(c->validity) + 1); if (commit_tree(&msg, tree_sha1, NULL, commit_sha1, NULL, NULL) < 0) return -1; if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL, 0, QUIET_ON_ERR) < 0) return -1; return 0; }
void *object_file_to_archive(const struct archiver_args *args, const char *path, const struct object_id *oid, unsigned int mode, enum object_type *type, unsigned long *sizep) { void *buffer; const struct commit *commit = args->convert ? args->commit : NULL; path += args->baselen; buffer = read_object_file(oid, type, sizep); if (buffer && S_ISREG(mode)) { struct strbuf buf = STRBUF_INIT; size_t size = 0; strbuf_attach(&buf, buffer, *sizep, *sizep + 1); convert_to_working_tree(args->repo->index, path, buf.buf, buf.len, &buf); if (commit) format_subst(commit, buf.buf, buf.len, &buf); buffer = strbuf_detach(&buf, &size); *sizep = size; } return buffer; }
/* * First, one directory to try is determined by the following algorithm. * * (0) If "strict" is given, the path is used as given and no DWIM is * done. Otherwise: * (1) "~/path" to mean path under the running user's home directory; * (2) "~user/path" to mean path under named user's home directory; * (3) "relative/path" to mean cwd relative directory; or * (4) "/absolute/path" to mean absolute directory. * * Unless "strict" is given, we check "%s/.git", "%s", "%s.git/.git", "%s.git" * in this order. We select the first one that is a valid git repository, and * chdir() to it. If none match, or we fail to chdir, we return NULL. * * If all goes well, we return the directory we used to chdir() (but * before ~user is expanded), avoiding getcwd() resolving symbolic * links. User relative paths are also returned as they are given, * except DWIM suffixing. */ const char *enter_repo(const char *path, int strict) { static struct strbuf validated_path = STRBUF_INIT; static struct strbuf used_path = STRBUF_INIT; if (!path) return NULL; if (!strict) { static const char *suffix[] = { "/.git", "", ".git/.git", ".git", NULL, }; const char *gitfile; int len = strlen(path); int i; while ((1 < len) && (path[len-1] == '/')) len--; /* * We can handle arbitrary-sized buffers, but this remains as a * sanity check on untrusted input. */ if (PATH_MAX <= len) return NULL; strbuf_reset(&used_path); strbuf_reset(&validated_path); strbuf_add(&used_path, path, len); strbuf_add(&validated_path, path, len); if (used_path.buf[0] == '~') { char *newpath = expand_user_path(used_path.buf); if (!newpath) return NULL; strbuf_attach(&used_path, newpath, strlen(newpath), strlen(newpath)); } for (i = 0; suffix[i]; i++) { struct stat st; size_t baselen = used_path.len; strbuf_addstr(&used_path, suffix[i]); if (!stat(used_path.buf, &st) && (S_ISREG(st.st_mode) || (S_ISDIR(st.st_mode) && is_git_directory(used_path.buf)))) { strbuf_addstr(&validated_path, suffix[i]); break; } strbuf_setlen(&used_path, baselen); } if (!suffix[i]) return NULL; gitfile = read_gitfile(used_path.buf); if (gitfile) { strbuf_reset(&used_path); strbuf_addstr(&used_path, gitfile); } if (chdir(used_path.buf)) return NULL; path = validated_path.buf; } else { const char *gitfile = read_gitfile(path); if (gitfile) path = gitfile; if (chdir(path)) return NULL; } if (is_git_directory(".")) { set_git_dir("."); check_repository_format(); return path; } return NULL; }
static int handle_cache(const char *path, unsigned char *sha1, const char *output) { mmfile_t mmfile[3] = {{NULL}}; mmbuffer_t result = {NULL, 0}; const struct cache_entry *ce; int pos, len, i, has_conflicts; struct rerere_io_mem io; int marker_size = ll_merge_marker_size(path); /* * Reproduce the conflicted merge in-core */ len = strlen(path); pos = cache_name_pos(path, len); if (0 <= pos) return -1; pos = -pos - 1; while (pos < active_nr) { enum object_type type; unsigned long size; ce = active_cache[pos++]; if (ce_namelen(ce) != len || memcmp(ce->name, path, len)) break; i = ce_stage(ce) - 1; if (!mmfile[i].ptr) { mmfile[i].ptr = read_object_file(&ce->oid, &type, &size); mmfile[i].size = size; } } for (i = 0; i < 3; i++) if (!mmfile[i].ptr && !mmfile[i].size) mmfile[i].ptr = xstrdup(""); /* * NEEDSWORK: handle conflicts from merges with * merge.renormalize set, too? */ ll_merge(&result, path, &mmfile[0], NULL, &mmfile[1], "ours", &mmfile[2], "theirs", NULL); for (i = 0; i < 3; i++) free(mmfile[i].ptr); memset(&io, 0, sizeof(io)); io.io.getline = rerere_mem_getline; if (output) io.io.output = fopen(output, "w"); else io.io.output = NULL; strbuf_init(&io.input, 0); strbuf_attach(&io.input, result.ptr, result.size, result.size); /* * Grab the conflict ID and optionally write the original * contents with conflict markers out. */ has_conflicts = handle_path(sha1, (struct rerere_io *)&io, marker_size); strbuf_release(&io.input); if (io.io.output) fclose(io.io.output); return has_conflicts; }
/* * Prepare a dummy commit that represents the work tree (or staged) item. * Note that annotating work tree item never works in the reverse. */ static struct commit *fake_working_tree_commit(struct diff_options *opt, const char *path, const char *contents_from) { struct commit *commit; struct blame_origin *origin; struct commit_list **parent_tail, *parent; struct object_id head_oid; struct strbuf buf = STRBUF_INIT; const char *ident; time_t now; int size, len; struct cache_entry *ce; unsigned mode; struct strbuf msg = STRBUF_INIT; read_cache(); time(&now); commit = alloc_commit_node(); commit->object.parsed = 1; commit->date = now; parent_tail = &commit->parents; if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL)) die("no such ref: HEAD"); parent_tail = append_parent(parent_tail, &head_oid); append_merge_parents(parent_tail); verify_working_tree_path(commit, path); origin = make_origin(commit, path); ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0); strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n"); for (parent = commit->parents; parent; parent = parent->next) strbuf_addf(&msg, "parent %s\n", oid_to_hex(&parent->item->object.oid)); strbuf_addf(&msg, "author %s\n" "committer %s\n\n" "Version of %s from %s\n", ident, ident, path, (!contents_from ? path : (!strcmp(contents_from, "-") ? "standard input" : contents_from))); set_commit_buffer_from_strbuf(commit, &msg); if (!contents_from || strcmp("-", contents_from)) { struct stat st; const char *read_from; char *buf_ptr; unsigned long buf_len; if (contents_from) { if (stat(contents_from, &st) < 0) die_errno("Cannot stat '%s'", contents_from); read_from = contents_from; } else { if (lstat(path, &st) < 0) die_errno("Cannot lstat '%s'", path); read_from = path; } mode = canon_mode(st.st_mode); switch (st.st_mode & S_IFMT) { case S_IFREG: if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) && textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len)) strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1); else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size) die_errno("cannot open or read '%s'", read_from); break; case S_IFLNK: if (strbuf_readlink(&buf, read_from, st.st_size) < 0) die_errno("cannot readlink '%s'", read_from); break; default: die("unsupported file type %s", read_from); } } else { /* Reading from stdin */ mode = 0; if (strbuf_read(&buf, 0, 0) < 0) die_errno("failed to read from stdin"); } convert_to_git(path, buf.buf, buf.len, &buf, 0); origin->file.ptr = buf.buf; origin->file.size = buf.len; pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash); /* * Read the current index, replace the path entry with * origin->blob_sha1 without mucking with its mode or type * bits; we are not going to write this index out -- we just * want to run "diff-index --cached". */ discard_cache(); read_cache(); len = strlen(path); if (!mode) { int pos = cache_name_pos(path, len); if (0 <= pos) mode = active_cache[pos]->ce_mode; else /* Let's not bother reading from HEAD tree */ mode = S_IFREG | 0644; } size = cache_entry_size(len); ce = xcalloc(1, size); oidcpy(&ce->oid, &origin->blob_oid); memcpy(ce->name, path, len); ce->ce_flags = create_ce_flags(0); ce->ce_namelen = len; ce->ce_mode = create_ce_mode(mode); add_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE); cache_tree_invalidate_path(&the_index, path); return commit; }
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; char *msg = strstr(partial_commit->buffer, "\n\n"); struct strbuf sb_msg = STRBUF_INIT; if (o->verbosity >= 3) printf("Committing notes in notes merge worktree at %.*s\n", 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)) { if (o->verbosity >= 3) printf("Skipping non-SHA1 entry '%s'\n", 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); if (o->verbosity >= 4) printf("Added resolved note for object %s: %s\n", sha1_to_hex(obj_sha1), sha1_to_hex(blob_sha1)); } strbuf_attach(&sb_msg, msg, strlen(msg), strlen(msg) + 1); create_notes_commit(partial_tree, partial_commit->parents, &sb_msg, result_sha1); if (o->verbosity >= 4) printf("Finalized notes merge commit: %s\n", sha1_to_hex(result_sha1)); free(path); return 0; }