const char *git_author_info(int flag) { return fmt_ident(getenv("GIT_AUTHOR_NAME"), getenv("GIT_AUTHOR_EMAIL"), getenv("GIT_AUTHOR_DATE"), flag); }
static void determine_author_info(struct strbuf *author_ident) { char *name, *email, *date; struct ident_split author; name = getenv("GIT_AUTHOR_NAME"); email = getenv("GIT_AUTHOR_EMAIL"); date = getenv("GIT_AUTHOR_DATE"); if (author_message) { const char *a, *lb, *rb, *eol; size_t len; a = strstr(author_message_buffer, "\nauthor "); if (!a) die(_("invalid commit: %s"), author_message); lb = strchrnul(a + strlen("\nauthor "), '<'); rb = strchrnul(lb, '>'); eol = strchrnul(rb, '\n'); if (!*lb || !*rb || !*eol) die(_("invalid commit: %s"), author_message); if (lb == a + strlen("\nauthor ")) /* \nauthor <*****@*****.**> */ name = xcalloc(1, 1); else name = xmemdupz(a + strlen("\nauthor "), (lb - strlen(" ") - (a + strlen("\nauthor ")))); email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<"))); date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> "))); len = eol - (rb + strlen("> ")); date = xmalloc(len + 2); *date = '@'; memcpy(date + 1, rb + strlen("> "), len); date[len + 1] = '\0'; } if (force_author) { const char *lb = strstr(force_author, " <"); const char *rb = strchr(force_author, '>'); if (!lb || !rb) die(_("malformed --author parameter")); name = xstrndup(force_author, lb - force_author); email = xstrndup(lb + 2, rb - (lb + 2)); } if (force_date) date = force_date; strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT)); if (!split_ident_line(&author, author_ident->buf, author_ident->len) && sane_ident_split(&author)) { export_one("GIT_AUTHOR_NAME", author.name_begin, author.name_end, 0); export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0); export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@'); } }
const char *git_committer_info(int flag) { if (getenv("GIT_COMMITTER_NAME") && getenv("GIT_COMMITTER_EMAIL")) user_ident_explicitly_given = 1; return fmt_ident(getenv("GIT_COMMITTER_NAME"), getenv("GIT_COMMITTER_EMAIL"), getenv("GIT_COMMITTER_DATE"), flag); }
const char *git_committer_info(int flag) { if (getenv("GIT_COMMITTER_NAME")) committer_ident_explicitly_given |= IDENT_NAME_GIVEN; if (getenv("GIT_COMMITTER_EMAIL")) committer_ident_explicitly_given |= IDENT_MAIL_GIVEN; return fmt_ident(getenv("GIT_COMMITTER_NAME"), getenv("GIT_COMMITTER_EMAIL"), getenv("GIT_COMMITTER_DATE"), flag); }
const char *git_author_info(int flag) { if (getenv("GIT_AUTHOR_NAME")) author_ident_explicitly_given |= IDENT_NAME_GIVEN; if (getenv("GIT_AUTHOR_EMAIL")) author_ident_explicitly_given |= IDENT_MAIL_GIVEN; return fmt_ident(getenv("GIT_AUTHOR_NAME"), getenv("GIT_AUTHOR_EMAIL"), getenv("GIT_AUTHOR_DATE"), flag); }
static void determine_author_info(struct strbuf *author_ident) { char *name, *email, *date; name = getenv("GIT_AUTHOR_NAME"); email = getenv("GIT_AUTHOR_EMAIL"); date = getenv("GIT_AUTHOR_DATE"); if (author_message) { const char *a, *lb, *rb, *eol; a = strstr(author_message_buffer, "\nauthor "); if (!a) die(_("invalid commit: %s"), author_message); lb = strchrnul(a + strlen("\nauthor "), '<'); rb = strchrnul(lb, '>'); eol = strchrnul(rb, '\n'); if (!*lb || !*rb || !*eol) die(_("invalid commit: %s"), author_message); if (lb == a + strlen("\nauthor ")) /* \nauthor <*****@*****.**> */ name = xcalloc(1, 1); else name = xmemdupz(a + strlen("\nauthor "), (lb - strlen(" ") - (a + strlen("\nauthor ")))); email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<"))); date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> "))); } if (force_author) { const char *lb = strstr(force_author, " <"); const char *rb = strchr(force_author, '>'); if (!lb || !rb) die(_("malformed --author parameter")); name = xstrndup(force_author, lb - force_author); email = xstrndup(lb + 2, rb - (lb + 2)); } if (force_date) date = force_date; strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_ERROR_ON_NO_NAME)); }
const char *fmt_name(const char *name, const char *email) { return fmt_ident(name, email, NULL, IDENT_STRICT | IDENT_NO_DATE); }
/* * 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; }
const char *fmt_name(const char *name, const char *email) { return fmt_ident(name, email, NULL, IDENT_ERROR_ON_NO_NAME | IDENT_NO_DATE); }
int cmd_commit(int argc, const char **argv, const char *prefix) { int header_len; struct strbuf sb; const char *index_file, *reflog_msg; char *nl, *p; unsigned char commit_sha1[20]; struct ref_lock *ref_lock; git_config(git_commit_config); argc = parse_and_validate_options(argc, argv, builtin_commit_usage); index_file = prepare_index(argc, argv, prefix); /* Set up everything for writing the commit object. This includes running hooks, writing the trees, and interacting with the user. */ if (!prepare_to_commit(index_file, prefix)) { rollback_index_files(); return 1; } /* * The commit object */ strbuf_init(&sb, 0); strbuf_addf(&sb, "tree %s\n", sha1_to_hex(active_cache_tree->sha1)); /* Determine parents */ if (initial_commit) { reflog_msg = "commit (initial)"; } else if (amend) { struct commit_list *c; struct commit *commit; reflog_msg = "commit (amend)"; commit = lookup_commit(head_sha1); if (!commit || parse_commit(commit)) die("could not parse HEAD commit"); for (c = commit->parents; c; c = c->next) add_parent(&sb, c->item->object.sha1); } else if (in_merge) { struct strbuf m; FILE *fp; reflog_msg = "commit (merge)"; add_parent(&sb, head_sha1); strbuf_init(&m, 0); fp = fopen(git_path("MERGE_HEAD"), "r"); if (fp == NULL) die("could not open %s for reading: %s", git_path("MERGE_HEAD"), strerror(errno)); while (strbuf_getline(&m, fp, '\n') != EOF) { unsigned char sha1[20]; if (get_sha1_hex(m.buf, sha1) < 0) die("Corrupt MERGE_HEAD file (%s)", m.buf); add_parent(&sb, sha1); } fclose(fp); strbuf_release(&m); } else { reflog_msg = "commit"; strbuf_addf(&sb, "parent %s\n", sha1_to_hex(head_sha1)); } strbuf_addf(&sb, "author %s\n", fmt_ident(author_name, author_email, author_date, IDENT_ERROR_ON_NO_NAME)); strbuf_addf(&sb, "committer %s\n", git_committer_info(IDENT_ERROR_ON_NO_NAME)); if (!is_encoding_utf8(git_commit_encoding)) strbuf_addf(&sb, "encoding %s\n", git_commit_encoding); strbuf_addch(&sb, '\n'); /* Finally, get the commit message */ header_len = sb.len; if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) { rollback_index_files(); die("could not read commit message"); } /* Truncate the message just before the diff, if any. */ p = strstr(sb.buf, "\ndiff --git a/"); if (p != NULL) strbuf_setlen(&sb, p - sb.buf + 1); if (cleanup_mode != CLEANUP_NONE) stripspace(&sb, cleanup_mode == CLEANUP_ALL); if (sb.len < header_len || message_is_empty(&sb, header_len)) { rollback_index_files(); die("no commit message? aborting commit."); } strbuf_addch(&sb, '\0'); if (is_encoding_utf8(git_commit_encoding) && !is_utf8(sb.buf)) fprintf(stderr, commit_utf8_warn); if (write_sha1_file(sb.buf, sb.len - 1, commit_type, commit_sha1)) { rollback_index_files(); die("failed to write commit object"); } ref_lock = lock_any_ref_for_update("HEAD", initial_commit ? NULL : head_sha1, 0); nl = strchr(sb.buf + header_len, '\n'); if (nl) strbuf_setlen(&sb, nl + 1 - sb.buf); else strbuf_addch(&sb, '\n'); strbuf_remove(&sb, 0, header_len); strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg)); strbuf_insert(&sb, strlen(reflog_msg), ": ", 2); if (!ref_lock) { rollback_index_files(); die("cannot lock HEAD ref"); } if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) { rollback_index_files(); die("cannot update HEAD ref"); } unlink(git_path("MERGE_HEAD")); unlink(git_path("MERGE_MSG")); unlink(git_path("SQUASH_MSG")); if (commit_index_files()) die ("Repository has been updated, but unable to write\n" "new_index file. Check that disk is not full or quota is\n" "not exceeded, and then \"git reset HEAD\" to recover."); rerere(); run_hook(get_index_file(), "post-commit", NULL); if (!quiet) print_summary(prefix, commit_sha1); return 0; }
int cmd_commit(int argc, const char **argv, const char *prefix) { struct strbuf sb = STRBUF_INIT; const char *index_file, *reflog_msg; char *nl, *p; unsigned char commit_sha1[20]; struct ref_lock *ref_lock; struct commit_list *parents = NULL, **pptr = &parents; struct stat statbuf; int allow_fast_forward = 1; struct wt_status s; wt_status_prepare(&s); git_config(git_commit_config, &s); in_merge = file_exists(git_path("MERGE_HEAD")); s.in_merge = in_merge; if (s.use_color == -1) s.use_color = git_use_color_default; argc = parse_and_validate_options(argc, argv, builtin_commit_usage, prefix, &s); if (dry_run) { if (diff_use_color_default == -1) diff_use_color_default = git_use_color_default; return dry_run_commit(argc, argv, prefix, &s); } index_file = prepare_index(argc, argv, prefix, 0); /* Set up everything for writing the commit object. This includes running hooks, writing the trees, and interacting with the user. */ if (!prepare_to_commit(index_file, prefix, &s)) { rollback_index_files(); return 1; } /* Determine parents */ reflog_msg = getenv("GIT_REFLOG_ACTION"); if (initial_commit) { if (!reflog_msg) reflog_msg = "commit (initial)"; } else if (amend) { struct commit_list *c; struct commit *commit; if (!reflog_msg) reflog_msg = "commit (amend)"; commit = lookup_commit(head_sha1); if (!commit || parse_commit(commit)) die("could not parse HEAD commit"); for (c = commit->parents; c; c = c->next) pptr = &commit_list_insert(c->item, pptr)->next; } else if (in_merge) { struct strbuf m = STRBUF_INIT; FILE *fp; if (!reflog_msg) reflog_msg = "commit (merge)"; pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next; fp = fopen(git_path("MERGE_HEAD"), "r"); if (fp == NULL) die_errno("could not open '%s' for reading", git_path("MERGE_HEAD")); while (strbuf_getline(&m, fp, '\n') != EOF) { unsigned char sha1[20]; if (get_sha1_hex(m.buf, sha1) < 0) die("Corrupt MERGE_HEAD file (%s)", m.buf); pptr = &commit_list_insert(lookup_commit(sha1), pptr)->next; } fclose(fp); strbuf_release(&m); if (!stat(git_path("MERGE_MODE"), &statbuf)) { if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0) die_errno("could not read MERGE_MODE"); if (!strcmp(sb.buf, "no-ff")) allow_fast_forward = 0; } if (allow_fast_forward) parents = reduce_heads(parents); } else { if (!reflog_msg) reflog_msg = "commit"; pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next; } /* Finally, get the commit message */ strbuf_reset(&sb); if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) { int saved_errno = errno; rollback_index_files(); die("could not read commit message: %s", strerror(saved_errno)); } /* Truncate the message just before the diff, if any. */ if (verbose) { p = strstr(sb.buf, "\ndiff --git "); if (p != NULL) strbuf_setlen(&sb, p - sb.buf + 1); } if (cleanup_mode != CLEANUP_NONE) stripspace(&sb, cleanup_mode == CLEANUP_ALL); if (message_is_empty(&sb) && !allow_empty_message) { rollback_index_files(); fprintf(stderr, "Aborting commit due to empty commit message.\n"); exit(1); } if (commit_tree(sb.buf, active_cache_tree->sha1, parents, commit_sha1, fmt_ident(author_name, author_email, author_date, IDENT_ERROR_ON_NO_NAME))) { rollback_index_files(); die("failed to write commit object"); } ref_lock = lock_any_ref_for_update("HEAD", initial_commit ? NULL : head_sha1, 0); nl = strchr(sb.buf, '\n'); if (nl) strbuf_setlen(&sb, nl + 1 - sb.buf); else strbuf_addch(&sb, '\n'); strbuf_insert(&sb, 0, reflog_msg, strlen(reflog_msg)); strbuf_insert(&sb, strlen(reflog_msg), ": ", 2); if (!ref_lock) { rollback_index_files(); die("cannot lock HEAD ref"); } if (write_ref_sha1(ref_lock, commit_sha1, sb.buf) < 0) { rollback_index_files(); die("cannot update HEAD ref"); } unlink(git_path("MERGE_HEAD")); unlink(git_path("MERGE_MSG")); unlink(git_path("MERGE_MODE")); unlink(git_path("SQUASH_MSG")); if (commit_index_files()) die ("Repository has been updated, but unable to write\n" "new_index file. Check that disk is not full or quota is\n" "not exceeded, and then \"git reset HEAD\" to recover."); rerere(0); run_hook(get_index_file(), "post-commit", NULL); if (amend && !no_post_rewrite) { struct notes_rewrite_cfg *cfg; cfg = init_copy_notes_for_rewrite("amend"); if (cfg) { copy_note_for_rewrite(cfg, head_sha1, commit_sha1); finish_copy_notes_for_rewrite(cfg); } run_rewrite_hook(head_sha1, commit_sha1); } if (!quiet) print_summary(prefix, commit_sha1); return 0; }