Created
May 3, 2010 15:19
-
-
Save ehiggs/388182 to your computer and use it in GitHub Desktop.
Git message editing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static int prepare_to_commit(const char *index_file, const char *prefix, | |
struct wt_status *s) | |
{ | |
struct stat statbuf; | |
int commitable, saved_color_setting; | |
struct strbuf sb = STRBUF_INIT; | |
char *buffer; | |
FILE *fp; | |
const char *hook_arg1 = NULL; | |
const char *hook_arg2 = NULL; | |
int ident_shown = 0; | |
if (!no_verify && run_hook(index_file, "pre-commit", NULL)) | |
return 0; | |
if (message.len) { | |
strbuf_addbuf(&sb, &message); | |
hook_arg1 = "message"; | |
} else if (logfile && !strcmp(logfile, "-")) { | |
if (isatty(0)) | |
fprintf(stderr, "(reading log message from standard input)\n"); | |
if (strbuf_read(&sb, 0, 0) < 0) | |
die_errno("could not read log from standard input"); | |
hook_arg1 = "message"; | |
} else if (logfile) { | |
if (strbuf_read_file(&sb, logfile, 0) < 0) | |
die_errno("could not read log file '%s'", | |
logfile); | |
hook_arg1 = "message"; | |
} else if (use_message) { | |
buffer = strstr(use_message_buffer, "\n\n"); | |
if (!buffer || buffer[2] == '\0') | |
die("commit has empty message"); | |
strbuf_add(&sb, buffer + 2, strlen(buffer + 2)); | |
hook_arg1 = "commit"; | |
hook_arg2 = use_message; | |
} else if (!stat(git_path("MERGE_MSG"), &statbuf)) { | |
if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0) | |
die_errno("could not read MERGE_MSG"); | |
hook_arg1 = "merge"; | |
} else if (!stat(git_path("SQUASH_MSG"), &statbuf)) { | |
if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0) | |
die_errno("could not read SQUASH_MSG"); | |
hook_arg1 = "squash"; | |
} else if (template_file && !stat(template_file, &statbuf)) { | |
if (strbuf_read_file(&sb, template_file, 0) < 0) | |
die_errno("could not read '%s'", template_file); | |
hook_arg1 = "template"; | |
} | |
/* | |
* This final case does not modify the template message, | |
* it just sets the argument to the prepare-commit-msg hook. | |
*/ | |
else if (in_merge) | |
hook_arg1 = "merge"; | |
fp = fopen(git_path(commit_editmsg), "w"); | |
if (fp == NULL) | |
die_errno("could not open '%s'", git_path(commit_editmsg)); | |
if (cleanup_mode != CLEANUP_NONE) | |
stripspace(&sb, 0); | |
if (signoff) { | |
struct strbuf sob = STRBUF_INIT; | |
int i; | |
strbuf_addstr(&sob, sign_off_header); | |
strbuf_addstr(&sob, fmt_name(getenv("GIT_COMMITTER_NAME"), | |
getenv("GIT_COMMITTER_EMAIL"))); | |
strbuf_addch(&sob, '\n'); | |
for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--) | |
; /* do nothing */ | |
if (prefixcmp(sb.buf + i, sob.buf)) { | |
if (!i || !ends_rfc2822_footer(&sb)) | |
strbuf_addch(&sb, '\n'); | |
strbuf_addbuf(&sb, &sob); | |
} | |
strbuf_release(&sob); | |
} | |
if (fwrite(sb.buf, 1, sb.len, fp) < sb.len) | |
die_errno("could not write commit template"); | |
strbuf_release(&sb); | |
determine_author_info(); | |
/* This checks if committer ident is explicitly given */ | |
git_committer_info(0); | |
if (use_editor && include_status) { | |
char *author_ident; | |
const char *committer_ident; | |
if (in_merge) | |
fprintf(fp, | |
"#\n" | |
"# It looks like you may be committing a MERGE.\n" | |
"# If this is not correct, please remove the file\n" | |
"# %s\n" | |
"# and try again.\n" | |
"#\n", | |
git_path("MERGE_HEAD")); | |
fprintf(fp, | |
"\n" | |
"# Please enter the commit message for your changes."); | |
if (cleanup_mode == CLEANUP_ALL) | |
fprintf(fp, | |
" Lines starting\n" | |
"# with '#' will be ignored, and an empty" | |
" message aborts the commit.\n"); | |
else /* CLEANUP_SPACE, that is. */ | |
fprintf(fp, | |
" Lines starting\n" | |
"# with '#' will be kept; you may remove them" | |
" yourself if you want to.\n" | |
"# An empty message aborts the commit.\n"); | |
if (only_include_assumed) | |
fprintf(fp, "# %s\n", only_include_assumed); | |
author_ident = xstrdup(fmt_name(author_name, author_email)); | |
committer_ident = fmt_name(getenv("GIT_COMMITTER_NAME"), | |
getenv("GIT_COMMITTER_EMAIL")); | |
if (strcmp(author_ident, committer_ident)) | |
fprintf(fp, | |
"%s" | |
"# Author: %s\n", | |
ident_shown++ ? "" : "#\n", | |
author_ident); | |
free(author_ident); | |
if (!user_ident_sufficiently_given()) | |
fprintf(fp, | |
"%s" | |
"# Committer: %s\n", | |
ident_shown++ ? "" : "#\n", | |
committer_ident); | |
if (ident_shown) | |
fprintf(fp, "#\n"); | |
saved_color_setting = s->use_color; | |
s->use_color = 0; | |
commitable = run_status(fp, index_file, prefix, 1, s); | |
s->use_color = saved_color_setting; | |
} else { | |
unsigned char sha1[20]; | |
const char *parent = "HEAD"; | |
if (!active_nr && read_cache() < 0) | |
die("Cannot read index"); | |
if (amend) | |
parent = "HEAD^1"; | |
if (get_sha1(parent, sha1)) | |
commitable = !!active_nr; | |
else | |
commitable = index_differs_from(parent, 0); | |
} | |
fclose(fp); | |
if (!commitable && !in_merge && !allow_empty && | |
!(amend && is_a_merge(head_sha1))) { | |
run_status(stdout, index_file, prefix, 0, s); | |
return 0; | |
} | |
/* | |
* Re-read the index as pre-commit hook could have updated it, | |
* and write it out as a tree. We must do this before we invoke | |
* the editor and after we invoke run_status above. | |
*/ | |
discard_cache(); | |
read_cache_from(index_file); | |
if (!active_cache_tree) | |
active_cache_tree = cache_tree(); | |
if (cache_tree_update(active_cache_tree, | |
active_cache, active_nr, 0, 0) < 0) { | |
error("Error building trees"); | |
return 0; | |
} | |
if (run_hook(index_file, "prepare-commit-msg", | |
git_path(commit_editmsg), hook_arg1, hook_arg2, NULL)) | |
return 0; | |
if (use_editor) { | |
char index[PATH_MAX]; | |
const char *env[2] = { index, NULL }; | |
snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", index_file); | |
if (launch_editor(git_path(commit_editmsg), NULL, env)) { | |
fprintf(stderr, | |
"Please supply the message using either -m or -F option.\n"); | |
exit(1); | |
} | |
} | |
if (!no_verify && | |
run_hook(index_file, "commit-msg", git_path(commit_editmsg), NULL)) { | |
return 0; | |
} | |
return 1; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef DEFAULT_EDITOR | |
#define DEFAULT_EDITOR "vi" | |
#endif | |
const char *git_editor(void) | |
{ | |
const char *editor = getenv("GIT_EDITOR"); | |
const char *terminal = getenv("TERM"); | |
int terminal_is_dumb = !terminal || !strcmp(terminal, "dumb"); | |
if (!editor && editor_program) | |
editor = editor_program; | |
if (!editor && !terminal_is_dumb) | |
editor = getenv("VISUAL"); | |
if (!editor) | |
editor = getenv("EDITOR"); | |
if (!editor && terminal_is_dumb) | |
return NULL; | |
if (!editor) | |
editor = DEFAULT_EDITOR; | |
return editor; | |
} | |
int launch_editor(const char *path, struct strbuf *buffer, const char *const *env) | |
{ | |
const char *editor = git_editor(); | |
if (!editor) | |
return error("Terminal is dumb, but EDITOR unset"); | |
if (strcmp(editor, ":")) { | |
const char *args[] = { editor, path, NULL }; | |
if (run_command_v_opt_cd_env(args, RUN_USING_SHELL, NULL, env)) | |
return error("There was a problem with the editor '%s'.", | |
editor); | |
} | |
if (!buffer) | |
return 0; | |
if (strbuf_read_file(buffer, path, 0) < 0) | |
return error("could not read file '%s': %s", | |
path, strerror(errno)); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* I'm tired of doing "vsnprintf()" etc just to open a | |
* file, so here's a "return static buffer with printf" | |
* interface for paths. | |
* | |
* It's obviously not thread-safe. Sue me. But it's quite | |
* useful for doing things like | |
* | |
* f = open(mkpath("%s/%s.git", base, name), O_RDONLY); | |
* | |
* which is what it's designed for. | |
*/ | |
/* | |
* snip | |
*/ | |
char *git_path(const char *fmt, ...) | |
{ | |
const char *git_dir = get_git_dir(); | |
char *pathname = get_pathname(); | |
va_list args; | |
unsigned len; | |
len = strlen(git_dir); | |
if (len > PATH_MAX-100) | |
return bad_path; | |
memcpy(pathname, git_dir, len); | |
if (len && git_dir[len-1] != '/') | |
pathname[len++] = '/'; | |
va_start(args, fmt); | |
len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args); | |
va_end(args); | |
if (len >= PATH_MAX) | |
return bad_path; | |
return cleanup_path(pathname); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment