static char *logmsg_reencode(const struct commit *commit, const char *output_encoding) { static const char *utf8 = "UTF-8"; const char *use_encoding; char *encoding; char *out; if (!*output_encoding) return NULL; encoding = get_header(commit, "encoding"); use_encoding = encoding ? encoding : utf8; if (!strcmp(use_encoding, output_encoding)) if (encoding) /* we'll strip encoding header later */ out = xstrdup(commit->buffer); else return NULL; /* nothing to do */ else out = reencode_string(commit->buffer, output_encoding, use_encoding); if (out) out = replace_encoding_header(out, output_encoding); free(encoding); return out; }
void format_note(struct notes_tree *t, const unsigned char *object_sha1, struct strbuf *sb, const char *output_encoding, int flags) { static const char utf8[] = "utf-8"; const unsigned char *sha1; char *msg, *msg_p; unsigned long linelen, msglen; enum object_type type; if (!t) t = &default_notes_tree; if (!t->initialized) init_notes(t, NULL, NULL, 0); sha1 = get_note(t, object_sha1); if (!sha1) return; if (!(msg = read_sha1_file(sha1, &type, &msglen)) || !msglen || type != OBJ_BLOB) { free(msg); return; } if (output_encoding && *output_encoding && strcmp(utf8, output_encoding)) { char *reencoded = reencode_string(msg, output_encoding, utf8); if (reencoded) { free(msg); msg = reencoded; msglen = strlen(msg); } } /* we will end the annotation by a newline anyway */ if (msglen && msg[msglen - 1] == '\n') msglen--; if (flags & NOTES_SHOW_HEADER) strbuf_addstr(sb, "\nNotes:\n"); for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) { linelen = strchrnul(msg_p, '\n') - msg_p; if (flags & NOTES_INDENT) strbuf_addstr(sb, " "); strbuf_add(sb, msg_p, linelen); strbuf_addch(sb, '\n'); } free(msg); }
static int get_message(const char *raw_message, struct commit_message *out) { const char *encoding; const char *p, *abbrev, *eol; char *q; int abbrev_len, oneline_len; if (!raw_message) return -1; encoding = get_encoding(raw_message); if (!encoding) encoding = "UTF-8"; if (!git_commit_encoding) git_commit_encoding = "UTF-8"; out->reencoded_message = NULL; out->message = raw_message; if (strcmp(encoding, git_commit_encoding)) out->reencoded_message = reencode_string(raw_message, git_commit_encoding, encoding); if (out->reencoded_message) out->message = out->reencoded_message; abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV); abbrev_len = strlen(abbrev); /* Find beginning and end of commit subject. */ p = out->message; while (*p && (*p != '\n' || p[1] != '\n')) p++; if (*p) { p += 2; for (eol = p + 1; *eol && *eol != '\n'; eol++) ; /* do nothing */ } else eol = p; oneline_len = eol - p; out->parent_label = xmalloc(strlen("parent of ") + abbrev_len + strlen("... ") + oneline_len + 1); q = out->parent_label; q = mempcpy(q, "parent of ", strlen("parent of ")); out->label = q; q = mempcpy(q, abbrev, abbrev_len); q = mempcpy(q, "... ", strlen("... ")); out->subject = q; q = mempcpy(q, p, oneline_len); *q = '\0'; return 0; }
const char *reencode(char **txt, const char *src_enc, const char *dst_enc) { char *tmp; if (!txt || !*txt || !src_enc || !dst_enc) return *txt; tmp = reencode_string(*txt, src_enc, dst_enc); if (tmp) { free(*txt); *txt = tmp; } return *txt; }
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 void convert_to_utf8(char *line, unsigned linesize, const char *charset) { char *out; if (!charset || !*charset) { charset = guess_charset(line, metainfo_charset); if (!charset) return; } if (!strcmp(metainfo_charset, charset)) return; out = reencode_string(line, metainfo_charset, charset); if (!out) die("cannot convert from %s to %s\n", charset, metainfo_charset); strlcpy(line, out, linesize); free(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 int get_message(const char *raw_message, struct commit_message *out) { const char *encoding; const char *abbrev, *subject; int abbrev_len, subject_len; char *q; if (!raw_message) return -1; encoding = get_encoding(raw_message); if (!encoding) encoding = "UTF-8"; if (!git_commit_encoding) git_commit_encoding = "UTF-8"; out->reencoded_message = NULL; out->message = raw_message; if (strcmp(encoding, git_commit_encoding)) out->reencoded_message = reencode_string(raw_message, git_commit_encoding, encoding); if (out->reencoded_message) out->message = out->reencoded_message; else out->message = raw_message; abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV); abbrev_len = strlen(abbrev); subject_len = find_commit_subject(out->message, &subject); out->parent_label = xmalloc(strlen("parent of ") + abbrev_len + strlen("... ") + subject_len + 1); q = out->parent_label; q = mempcpy(q, "parent of ", strlen("parent of ")); out->label = q; q = mempcpy(q, abbrev, abbrev_len); q = mempcpy(q, "... ", strlen("... ")); out->subject = q; q = mempcpy(q, subject, subject_len); *q = '\0'; return 0; }
static const char *reencode(char **txt, const char *src_enc, const char *dst_enc) { char *tmp; if (!txt) return NULL; if (!*txt || !src_enc || !dst_enc) return *txt; /* no encoding needed if src_enc equals dst_enc */ if (!strcasecmp(src_enc, dst_enc)) return *txt; tmp = reencode_string(*txt, dst_enc, src_enc); if (tmp) { free(*txt); *txt = tmp; } return *txt; }
/* * Fill the given strbuf with the notes associated with the given object. * * If the given notes_tree structure is not initialized, it will be auto- * initialized to the default value (see documentation for init_notes() above). * If the given notes_tree is NULL, the internal/default notes_tree will be * used instead. * * (raw != 0) gives the %N userformat; otherwise, the note message is given * for human consumption. */ static void format_note(struct notes_tree *t, const struct object_id *object_oid, struct strbuf *sb, const char *output_encoding, int raw) { static const char utf8[] = "utf-8"; const struct object_id *oid; char *msg, *msg_p; unsigned long linelen, msglen; enum object_type type; if (!t) t = &default_notes_tree; if (!t->initialized) init_notes(t, NULL, NULL, 0); oid = get_note(t, object_oid); if (!oid) return; if (!(msg = read_object_file(oid, &type, &msglen)) || type != OBJ_BLOB) { free(msg); return; } if (output_encoding && *output_encoding && !is_encoding_utf8(output_encoding)) { char *reencoded = reencode_string(msg, output_encoding, utf8); if (reencoded) { free(msg); msg = reencoded; msglen = strlen(msg); } } /* we will end the annotation by a newline anyway */ if (msglen && msg[msglen - 1] == '\n') msglen--; if (!raw) { const char *ref = t->ref; if (!ref || !strcmp(ref, GIT_NOTES_DEFAULT_REF)) { strbuf_addstr(sb, "\nNotes:\n"); } else { if (starts_with(ref, "refs/")) ref += 5; if (starts_with(ref, "notes/")) ref += 6; strbuf_addf(sb, "\nNotes (%s):\n", ref); } } for (msg_p = msg; msg_p < msg + msglen; msg_p += linelen + 1) { linelen = strchrnul(msg_p, '\n') - msg_p; if (!raw) strbuf_addstr(sb, " "); strbuf_add(sb, msg_p, linelen); strbuf_addch(sb, '\n'); } free(msg); }
const char *logmsg_reencode(const struct commit *commit, char **commit_encoding, const char *output_encoding) { static const char *utf8 = "UTF-8"; const char *use_encoding; char *encoding; const char *msg = get_commit_buffer(commit, NULL); char *out; if (!output_encoding || !*output_encoding) { if (commit_encoding) *commit_encoding = get_header(commit, msg, "encoding"); return msg; } encoding = get_header(commit, msg, "encoding"); if (commit_encoding) *commit_encoding = encoding; use_encoding = encoding ? encoding : utf8; if (same_encoding(use_encoding, output_encoding)) { /* * No encoding work to be done. If we have no encoding header * at all, then there's nothing to do, and we can return the * message verbatim (whether newly allocated or not). */ if (!encoding) return msg; /* * Otherwise, we still want to munge the encoding header in the * result, which will be done by modifying the buffer. If we * are using a fresh copy, we can reuse it. But if we are using * the cached copy from get_commit_buffer, we need to duplicate it * to avoid munging the cached copy. */ if (msg == get_cached_commit_buffer(commit, NULL)) out = xstrdup(msg); else out = (char *)msg; } else { /* * There's actual encoding work to do. Do the reencoding, which * still leaves the header to be replaced in the next step. At * this point, we are done with msg. If we allocated a fresh * copy, we can free it. */ out = reencode_string(msg, output_encoding, use_encoding); if (out) unuse_commit_buffer(commit, msg); } /* * This replacement actually consumes the buffer we hand it, so we do * not have to worry about freeing the old "out" here. */ if (out) out = replace_encoding_header(out, output_encoding); if (!commit_encoding) free(encoding); /* * If the re-encoding failed, out might be NULL here; in that * case we just return the commit message verbatim. */ return out ? out : msg; }
static void handle_commit(struct commit *commit, struct rev_info *rev) { int saved_output_format = rev->diffopt.output_format; const char *commit_buffer; const char *author, *author_end, *committer, *committer_end; const char *encoding, *message; char *reencoded = NULL; struct commit_list *p; const char *refname; int i; rev->diffopt.output_format = DIFF_FORMAT_CALLBACK; parse_commit_or_die(commit); commit_buffer = get_commit_buffer(commit, NULL); author = strstr(commit_buffer, "\nauthor "); if (!author) die ("Could not find author in commit %s", sha1_to_hex(commit->object.sha1)); author++; author_end = strchrnul(author, '\n'); committer = strstr(author_end, "\ncommitter "); if (!committer) die ("Could not find committer in commit %s", sha1_to_hex(commit->object.sha1)); committer++; committer_end = strchrnul(committer, '\n'); message = strstr(committer_end, "\n\n"); encoding = find_encoding(committer_end, message); if (message) message += 2; if (commit->parents && get_object_mark(&commit->parents->item->object) != 0 && !full_tree) { parse_commit_or_die(commit->parents->item); diff_tree_sha1(commit->parents->item->tree->object.sha1, commit->tree->object.sha1, "", &rev->diffopt); } else diff_root_tree_sha1(commit->tree->object.sha1, "", &rev->diffopt); /* Export the referenced blobs, and remember the marks. */ for (i = 0; i < diff_queued_diff.nr; i++) if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode)) export_blob(diff_queued_diff.queue[i]->two->sha1); refname = commit->util; if (anonymize) { refname = anonymize_refname(refname); anonymize_ident_line(&committer, &committer_end); anonymize_ident_line(&author, &author_end); } mark_next_object(&commit->object); if (anonymize) reencoded = anonymize_commit_message(message); else if (!is_encoding_utf8(encoding)) reencoded = reencode_string(message, "UTF-8", encoding); if (!commit->parents) printf("reset %s\n", refname); printf("commit %s\nmark :%"PRIu32"\n%.*s\n%.*s\ndata %u\n%s", refname, last_idnum, (int)(author_end - author), author, (int)(committer_end - committer), committer, (unsigned)(reencoded ? strlen(reencoded) : message ? strlen(message) : 0), reencoded ? reencoded : message ? message : ""); free(reencoded); unuse_commit_buffer(commit, commit_buffer); for (i = 0, p = commit->parents; p; p = p->next) { int mark = get_object_mark(&p->item->object); if (!mark) continue; if (i == 0) printf("from :%d\n", mark); else printf("merge :%d\n", mark); i++; } if (full_tree) printf("deleteall\n"); log_tree_diff_flush(rev); rev->diffopt.output_format = saved_output_format; printf("\n"); show_progress(); }
static int parse_and_validate_options(int argc, const char *argv[], const char * const usage[]) { int f = 0; argc = parse_options(argc, argv, builtin_commit_options, usage, 0); if (logfile || message.len || use_message) use_editor = 0; if (edit_flag) use_editor = 1; if (!use_editor) setenv("GIT_EDITOR", ":", 1); if (get_sha1("HEAD", head_sha1)) initial_commit = 1; if (!get_sha1("MERGE_HEAD", merge_head_sha1)) in_merge = 1; /* Sanity check options */ if (amend && initial_commit) die("You have nothing to amend."); if (amend && in_merge) die("You are in the middle of a merge -- cannot amend."); if (use_message) f++; if (edit_message) f++; if (logfile) f++; if (f > 1) die("Only one of -c/-C/-F can be used."); if (message.len && f > 0) die("Option -m cannot be combined with -c/-C/-F."); if (edit_message) use_message = edit_message; if (amend && !use_message) use_message = "HEAD"; if (use_message) { unsigned char sha1[20]; static char utf8[] = "UTF-8"; const char *out_enc; char *enc, *end; struct commit *commit; if (get_sha1(use_message, sha1)) die("could not lookup commit %s", use_message); commit = lookup_commit_reference(sha1); if (!commit || parse_commit(commit)) die("could not parse commit %s", use_message); enc = strstr(commit->buffer, "\nencoding"); if (enc) { end = strchr(enc + 10, '\n'); enc = xstrndup(enc + 10, end - (enc + 10)); } else { enc = utf8; } out_enc = git_commit_encoding ? git_commit_encoding : utf8; if (strcmp(out_enc, enc)) use_message_buffer = reencode_string(commit->buffer, out_enc, enc); /* * If we failed to reencode the buffer, just copy it * byte for byte so the user can try to fix it up. * This also handles the case where input and output * encodings are identical. */ if (use_message_buffer == NULL) use_message_buffer = xstrdup(commit->buffer); if (enc != utf8) free(enc); } if (!!also + !!only + !!all + !!interactive > 1) die("Only one of --include/--only/--all/--interactive can be used."); if (argc == 0 && (also || (only && !amend))) die("No paths with --include/--only does not make sense."); if (argc == 0 && only && amend) only_include_assumed = "Clever... amending the last one with dirty index."; if (argc > 0 && !also && !only) only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths..."; if (!cleanup_arg || !strcmp(cleanup_arg, "default")) cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE; else if (!strcmp(cleanup_arg, "verbatim")) cleanup_mode = CLEANUP_NONE; else if (!strcmp(cleanup_arg, "whitespace")) cleanup_mode = CLEANUP_SPACE; else if (!strcmp(cleanup_arg, "strip")) cleanup_mode = CLEANUP_ALL; else die("Invalid cleanup mode %s", cleanup_arg); if (all && argc > 0) die("Paths with -a does not make sense."); else if (interactive && argc > 0) die("Paths with --interactive does not make sense."); return argc; }
static int revert_or_cherry_pick(int argc, const char **argv) { unsigned char head[20]; struct commit *base, *next, *parent; int i; char *oneline, *reencoded_message = NULL; const char *message, *encoding; const char *defmsg = xstrdup(git_path("MERGE_MSG")); git_config(git_default_config); me = action == REVERT ? "revert" : "cherry-pick"; setenv(GIT_REFLOG_ACTION, me, 0); parse_args(argc, argv); /* this is copied from the shell script, but it's never triggered... */ if (action == REVERT && !no_replay) die("revert is incompatible with replay"); if (no_commit) { /* * We do not intend to commit immediately. We just want to * merge the differences in, so let's compute the tree * that represents the "current" state for merge-recursive * to work on. */ if (write_cache_as_tree(head, 0, NULL)) die ("Your index file is unmerged."); } else { if (get_sha1("HEAD", head)) die ("You do not have a valid HEAD"); if (read_cache() < 0) die("could not read the index"); if (index_is_dirty()) die ("Dirty index: cannot %s", me); discard_cache(); } if (!commit->parents) die ("Cannot %s a root commit", me); if (commit->parents->next) { /* Reverting or cherry-picking a merge commit */ int cnt; struct commit_list *p; if (!mainline) die("Commit %s is a merge but no -m option was given.", sha1_to_hex(commit->object.sha1)); for (cnt = 1, p = commit->parents; cnt != mainline && p; cnt++) p = p->next; if (cnt != mainline || !p) die("Commit %s does not have parent %d", sha1_to_hex(commit->object.sha1), mainline); parent = p->item; } else if (0 < mainline) die("Mainline was specified but commit %s is not a merge.", sha1_to_hex(commit->object.sha1)); else parent = commit->parents->item; if (!(message = commit->buffer)) die ("Cannot get commit message for %s", sha1_to_hex(commit->object.sha1)); /* * "commit" is an existing commit. We would want to apply * the difference it introduces since its first parent "prev" * on top of the current HEAD if we are cherry-pick. Or the * reverse of it if we are revert. */ msg_fd = hold_lock_file_for_update(&msg_file, defmsg, 1); encoding = get_encoding(message); if (!encoding) encoding = "utf-8"; if (!git_commit_encoding) git_commit_encoding = "utf-8"; if ((reencoded_message = reencode_string(message, git_commit_encoding, encoding))) message = reencoded_message; oneline = get_oneline(message); if (action == REVERT) { char *oneline_body = strchr(oneline, ' '); base = commit; next = parent; add_to_msg("Revert \""); add_to_msg(oneline_body + 1); add_to_msg("\"\n\nThis reverts commit "); add_to_msg(sha1_to_hex(commit->object.sha1)); add_to_msg(".\n"); } else { base = parent; next = commit; set_author_ident_env(message); add_message_to_msg(message); if (no_replay) { add_to_msg("(cherry picked from commit "); add_to_msg(sha1_to_hex(commit->object.sha1)); add_to_msg(")\n"); } } if (merge_recursive(sha1_to_hex(base->object.sha1), sha1_to_hex(head), "HEAD", sha1_to_hex(next->object.sha1), oneline) || write_cache_as_tree(head, 0, NULL)) { add_to_msg("\nConflicts:\n\n"); read_cache(); for (i = 0; i < active_nr;) { struct cache_entry *ce = active_cache[i++]; if (ce_stage(ce)) { add_to_msg("\t"); add_to_msg(ce->name); add_to_msg("\n"); while (i < active_nr && !strcmp(ce->name, active_cache[i]->name)) i++; } } if (commit_lock_file(&msg_file) < 0) die ("Error wrapping up %s", defmsg); fprintf(stderr, "Automatic %s failed.%s\n", me, help_msg(commit->object.sha1)); exit(1); } if (commit_lock_file(&msg_file) < 0) die ("Error wrapping up %s", defmsg); fprintf(stderr, "Finished one %s.\n", me); /* * * If we are cherry-pick, and if the merge did not result in * hand-editing, we will hit this commit and inherit the original * author date and name. * If we are revert, or if our cherry-pick results in a hand merge, * we had better say that the current user is responsible for that. */ if (!no_commit) { /* 6 is max possible length of our args array including NULL */ const char *args[6]; int i = 0; args[i++] = "commit"; args[i++] = "-n"; if (signoff) args[i++] = "-s"; if (!edit) { args[i++] = "-F"; args[i++] = defmsg; } args[i] = NULL; return execv_git_cmd(args); } free(reencoded_message); return 0; }