*/
#include "cache.h"
+#include "lockfile.h"
#include "cache-tree.h"
#include "color.h"
#include "dir.h"
die_resolve_conflict("commit");
}
-static char *prepare_index(int argc, const char **argv, const char *prefix,
- const struct commit *current_head, int is_status)
+static const char *prepare_index(int argc, const char **argv, const char *prefix,
+ const struct commit *current_head, int is_status)
{
struct string_list partial;
struct pathspec pathspec;
die(_("unable to create temporary index"));
old_index_env = getenv(INDEX_ENVIRONMENT);
- setenv(INDEX_ENVIRONMENT, index_lock.filename, 1);
+ setenv(INDEX_ENVIRONMENT, index_lock.filename.buf, 1);
if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
die(_("interactive add failed"));
unsetenv(INDEX_ENVIRONMENT);
discard_cache();
- read_cache_from(index_lock.filename);
+ read_cache_from(index_lock.filename.buf);
if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
if (reopen_lock_file(&index_lock) < 0)
die(_("unable to write index file"));
warning(_("Failed to update main cache tree"));
commit_style = COMMIT_NORMAL;
- return index_lock.filename;
+ return index_lock.filename.buf;
}
/*
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
die(_("unable to write new_index file"));
commit_style = COMMIT_NORMAL;
- return index_lock.filename;
+ return index_lock.filename.buf;
}
/*
die(_("unable to write temporary index file"));
discard_cache();
- read_cache_from(false_lock.filename);
+ read_cache_from(false_lock.filename.buf);
- return false_lock.filename;
+ return false_lock.filename.buf;
}
static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
return 1;
}
-static int parse_force_date(const char *in, char *out, int len)
+static int parse_force_date(const char *in, struct strbuf *out)
{
- if (len < 1)
- return -1;
- *out++ = '@';
- len--;
+ strbuf_addch(out, '@');
- if (parse_date(in, out, len) < 0) {
+ if (parse_date(in, out) < 0) {
int errors = 0;
unsigned long t = approxidate_careful(in, &errors);
if (errors)
return -1;
- snprintf(out, len, "%lu", t);
+ strbuf_addf(out, "%lu", t);
}
return 0;
}
+static void set_ident_var(char **buf, char *val)
+{
+ free(*buf);
+ *buf = val;
+}
+
+static char *envdup(const char *var)
+{
+ const char *val = getenv(var);
+ return val ? xstrdup(val) : NULL;
+}
+
static void determine_author_info(struct strbuf *author_ident)
{
char *name, *email, *date;
struct ident_split author;
- char date_buf[64];
- name = getenv("GIT_AUTHOR_NAME");
- email = getenv("GIT_AUTHOR_EMAIL");
- date = getenv("GIT_AUTHOR_DATE");
+ name = envdup("GIT_AUTHOR_NAME");
+ email = envdup("GIT_AUTHOR_EMAIL");
+ date = envdup("GIT_AUTHOR_DATE");
if (author_message) {
- const char *a, *lb, *rb, *eol;
+ struct ident_split ident;
size_t len;
+ const char *a;
- a = strstr(author_message_buffer, "\nauthor ");
+ a = find_commit_header(author_message_buffer, "author", &len);
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 <foo@example.com> */
- name = xcalloc(1, 1);
- else
- name = xmemdupz(a + strlen("\nauthor "),
- (lb - strlen(" ") -
- (a + strlen("\nauthor "))));
- email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
- len = eol - (rb + strlen("> "));
- date = xmalloc(len + 2);
- *date = '@';
- memcpy(date + 1, rb + strlen("> "), len);
- date[len + 1] = '\0';
+ die(_("commit '%s' lacks author header"), author_message);
+ if (split_ident_line(&ident, a, len) < 0)
+ die(_("commit '%s' has malformed author line"), author_message);
+
+ set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin));
+ set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin));
+
+ if (ident.date_begin) {
+ struct strbuf date_buf = STRBUF_INIT;
+ strbuf_addch(&date_buf, '@');
+ strbuf_add(&date_buf, ident.date_begin, ident.date_end - ident.date_begin);
+ strbuf_addch(&date_buf, ' ');
+ strbuf_add(&date_buf, ident.tz_begin, ident.tz_end - ident.tz_begin);
+ set_ident_var(&date, strbuf_detach(&date_buf, NULL));
+ }
}
if (force_author) {
- const char *lb = strstr(force_author, " <");
- const char *rb = strchr(force_author, '>');
+ struct ident_split ident;
- if (!lb || !rb)
+ if (split_ident_line(&ident, force_author, strlen(force_author)) < 0)
die(_("malformed --author parameter"));
- name = xstrndup(force_author, lb - force_author);
- email = xstrndup(lb + 2, rb - (lb + 2));
+ set_ident_var(&name, xmemdupz(ident.name_begin, ident.name_end - ident.name_begin));
+ set_ident_var(&email, xmemdupz(ident.mail_begin, ident.mail_end - ident.mail_begin));
}
if (force_date) {
- if (parse_force_date(force_date, date_buf, sizeof(date_buf)))
+ struct strbuf date_buf = STRBUF_INIT;
+ if (parse_force_date(force_date, &date_buf))
die(_("invalid date format: %s"), force_date);
- date = date_buf;
+ set_ident_var(&date, strbuf_detach(&date_buf, NULL));
}
strbuf_addstr(author_ident, fmt_ident(name, email, date, IDENT_STRICT));
export_one("GIT_AUTHOR_EMAIL", author.mail_begin, author.mail_end, 0);
export_one("GIT_AUTHOR_DATE", author.date_begin, author.tz_end, '@');
}
+
+ free(name);
+ free(email);
+ free(date);
}
static void split_ident_or_die(struct ident_split *id, const struct strbuf *buf)
if (clean_message_contents)
stripspace(&sb, 0);
- if (signoff) {
- /*
- * See if we have a Conflicts: block at the end. If yes, count
- * its size, so we can ignore it.
- */
- int ignore_footer = 0;
- int i, eol, previous = 0;
- const char *nl;
-
- for (i = 0; i < sb.len; i++) {
- nl = memchr(sb.buf + i, '\n', sb.len - i);
- if (nl)
- eol = nl - sb.buf;
- else
- eol = sb.len;
- if (starts_with(sb.buf + previous, "\nConflicts:\n")) {
- ignore_footer = sb.len - previous;
- break;
- }
- while (i < eol)
- i++;
- previous = eol;
- }
-
- append_signoff(&sb, ignore_footer, 0);
- }
+ if (signoff)
+ append_signoff(&sb, ignore_non_trailer(&sb), 0);
if (fwrite(sb.buf, 1, sb.len, s->fp) < sb.len)
die_errno(_("could not write commit template"));
return commitable ? 0 : 1;
}
-static int parse_status_slot(const char *var, int offset)
+static int parse_status_slot(const char *slot)
{
- if (!strcasecmp(var+offset, "header"))
+ if (!strcasecmp(slot, "header"))
return WT_STATUS_HEADER;
- if (!strcasecmp(var+offset, "branch"))
+ if (!strcasecmp(slot, "branch"))
return WT_STATUS_ONBRANCH;
- if (!strcasecmp(var+offset, "updated")
- || !strcasecmp(var+offset, "added"))
+ if (!strcasecmp(slot, "updated") || !strcasecmp(slot, "added"))
return WT_STATUS_UPDATED;
- if (!strcasecmp(var+offset, "changed"))
+ if (!strcasecmp(slot, "changed"))
return WT_STATUS_CHANGED;
- if (!strcasecmp(var+offset, "untracked"))
+ if (!strcasecmp(slot, "untracked"))
return WT_STATUS_UNTRACKED;
- if (!strcasecmp(var+offset, "nobranch"))
+ if (!strcasecmp(slot, "nobranch"))
return WT_STATUS_NOBRANCH;
- if (!strcasecmp(var+offset, "unmerged"))
+ if (!strcasecmp(slot, "unmerged"))
return WT_STATUS_UNMERGED;
return -1;
}
static int git_status_config(const char *k, const char *v, void *cb)
{
struct wt_status *s = cb;
+ const char *slot_name;
if (starts_with(k, "column."))
return git_column_config(k, v, "status", &s->colopts);
s->display_comment_prefix = git_config_bool(k, v);
return 0;
}
- if (starts_with(k, "status.color.") || starts_with(k, "color.status.")) {
- int slot = parse_status_slot(k, 13);
+ if (skip_prefix(k, "status.color.", &slot_name) ||
+ skip_prefix(k, "color.status.", &slot_name)) {
+ int slot = parse_status_slot(slot_name);
if (slot < 0)
return 0;
if (!v)
return config_error_nonbool(k);
- color_parse(v, k, s->color_palette[slot]);
- return 0;
+ return color_parse(v, s->color_palette[slot]);
}
if (!strcmp(k, "status.relativepaths")) {
s->relative_paths = git_config_bool(k, v);
rev.diffopt.break_opt = 0;
diff_setup_done(&rev.diffopt);
- head = resolve_ref_unsafe("HEAD", junk_sha1, 0, NULL);
- printf("[%s%s ",
- starts_with(head, "refs/heads/") ?
- head + 11 :
- !strcmp(head, "HEAD") ?
- _("detached HEAD") :
- head,
- initial_commit ? _(" (root-commit)") : "");
+ head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL);
+ if (!strcmp(head, "HEAD"))
+ head = _("detached HEAD");
+ else
+ skip_prefix(head, "refs/heads/", &head);
+ printf("[%s%s ", head, initial_commit ? _(" (root-commit)") : "");
if (!log_tree_commit(&rev, commit)) {
rev.always_show_header = 1;
ref_transaction_update(transaction, "HEAD", sha1,
current_head
? current_head->object.sha1 : NULL,
- 0, !!current_head, &err) ||
- ref_transaction_commit(transaction, sb.buf, &err)) {
+ 0, !!current_head, sb.buf, &err) ||
+ ref_transaction_commit(transaction, &err)) {
rollback_index_files();
die("%s", err.buf);
}
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"
+ "new_index file. Check that disk is not full and quota is\n"
"not exceeded, and then \"git reset HEAD\" to recover."));
rerere(0);
#include "cache.h"
#include "parse-options.h"
#include "builtin.h"
+#include "lockfile.h"
#include "run-command.h"
#include "diff.h"
#include "refs.h"
#include "remote.h"
#include "fmt-merge-msg.h"
#include "gpg-interface.h"
+ #include "sequencer.h"
#define DEFAULT_TWOHEAD (1<<0)
#define DEFAULT_OCTOPUS (1<<1)
if (argc < 0)
die(_("Bad branch.%s.mergeoptions string: %s"), branch,
split_cmdline_strerror(argc));
- argv = xrealloc(argv, sizeof(*argv) * (argc + 2));
+ REALLOC_ARRAY(argv, argc + 2);
memmove(argv + 1, argv, sizeof(*argv) * (argc + 1));
argc++;
argv[0] = "branch.*.mergeoptions";
struct commit_list *remoteheads,
struct commit *head, const char *head_arg)
{
- struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+ static struct lock_file lock;
- hold_locked_index(lock, 1);
+ hold_locked_index(&lock, 1);
refresh_cache(REFRESH_QUIET);
if (active_cache_changed &&
- write_locked_index(&the_index, lock, COMMIT_LOCK))
+ write_locked_index(&the_index, &lock, COMMIT_LOCK))
return error(_("Unable to write index."));
- rollback_lock_file(lock);
+ rollback_lock_file(&lock);
if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
int clean, x;
struct commit *result;
- struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
struct commit_list *reversed = NULL;
struct merge_options o;
struct commit_list *j;
for (j = common; j; j = j->next)
commit_list_insert(j->item, &reversed);
- hold_locked_index(lock, 1);
+ hold_locked_index(&lock, 1);
clean = merge_recursive(&o, head,
remoteheads->item, reversed, &result);
if (active_cache_changed &&
- write_locked_index(&the_index, lock, COMMIT_LOCK))
+ write_locked_index(&the_index, &lock, COMMIT_LOCK))
die (_("unable to write %s"), get_index_file());
- rollback_lock_file(lock);
+ rollback_lock_file(&lock);
return clean ? 0 : 1;
} else {
return try_merge_command(strategy, xopts_nr, xopts,
return 0;
}
- static int suggest_conflicts(int renormalizing)
+ static int suggest_conflicts(void)
{
const char *filename;
FILE *fp;
- int pos;
+ struct strbuf msgbuf = STRBUF_INIT;
filename = git_path("MERGE_MSG");
fp = fopen(filename, "a");
if (!fp)
die_errno(_("Could not open '%s' for writing"), filename);
- fprintf(fp, "\nConflicts:\n");
- for (pos = 0; pos < active_nr; pos++) {
- const struct cache_entry *ce = active_cache[pos];
-
- if (ce_stage(ce)) {
- fprintf(fp, "\t%s\n", ce->name);
- while (pos + 1 < active_nr &&
- !strcmp(ce->name,
- active_cache[pos + 1]->name))
- pos++;
- }
- }
+
+ append_conflicts_hint(&msgbuf);
+ fputs(msgbuf.buf, fp);
fclose(fp);
rerere(allow_rerere_auto);
printf(_("Automatic merge failed; "
* Check if we are _not_ on a detached HEAD, i.e. if there is a
* current branch.
*/
- branch = branch_to_free = resolve_refdup("HEAD", head_sha1, 0, &flag);
+ branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, &flag);
if (branch && starts_with(branch, "refs/heads/"))
branch += 11;
if (!branch || is_null_sha1(head_sha1))
*/
if (advice_resolve_conflict)
die(_("You have not concluded your merge (MERGE_HEAD exists).\n"
- "Please, commit your changes before you can merge."));
+ "Please, commit your changes before you merge."));
else
die(_("You have not concluded your merge (MERGE_HEAD exists)."));
}
if (file_exists(git_path("CHERRY_PICK_HEAD"))) {
if (advice_resolve_conflict)
die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists).\n"
- "Please, commit your changes before you can merge."));
+ "Please, commit your changes before you merge."));
else
die(_("You have not concluded your cherry-pick (CHERRY_PICK_HEAD exists)."));
}
fprintf(stderr, _("Automatic merge went well; "
"stopped before committing as requested\n"));
else
- ret = suggest_conflicts(option_renormalize);
+ ret = suggest_conflicts();
done:
free(branch_to_free);
static void record_author_date(struct author_date_slab *author_date,
struct commit *commit)
{
- const char *buf, *line_end, *ident_line;
const char *buffer = get_commit_buffer(commit, NULL);
struct ident_split ident;
+ const char *ident_line;
+ size_t ident_len;
char *date_end;
unsigned long date;
- for (buf = buffer; buf; buf = line_end + 1) {
- line_end = strchrnul(buf, '\n');
- if (!skip_prefix(buf, "author ", &ident_line)) {
- if (!line_end[0] || line_end[1] == '\n')
- return; /* end of header */
- continue;
- }
- if (split_ident_line(&ident,
- ident_line, line_end - ident_line) ||
- !ident.date_begin || !ident.date_end)
- goto fail_exit; /* malformed "author" line */
- break;
- }
+ ident_line = find_commit_header(buffer, "author", &ident_len);
+ if (!ident_line)
+ goto fail_exit; /* no author line */
+ if (split_ident_line(&ident, ident_line, ident_len) ||
+ !ident.date_begin || !ident.date_end)
+ goto fail_exit; /* malformed "author" line */
date = strtoul(ident.date_begin, &date_end, 10);
if (date_end != ident.date_end)
free(buf);
}
-static struct {
- char result;
- const char *check;
-} sigcheck_gpg_status[] = {
- { 'G', "\n[GNUPG:] GOODSIG " },
- { 'B', "\n[GNUPG:] BADSIG " },
- { 'U', "\n[GNUPG:] TRUST_NEVER" },
- { 'U', "\n[GNUPG:] TRUST_UNDEFINED" },
-};
-
-static void parse_gpg_output(struct signature_check *sigc)
-{
- const char *buf = sigc->gpg_status;
- int i;
-
- /* Iterate over all search strings */
- for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
- const char *found, *next;
-
- if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
- found = strstr(buf, sigcheck_gpg_status[i].check);
- if (!found)
- continue;
- found += strlen(sigcheck_gpg_status[i].check);
- }
- sigc->result = sigcheck_gpg_status[i].result;
- /* The trust messages are not followed by key/signer information */
- if (sigc->result != 'U') {
- sigc->key = xmemdupz(found, 16);
- found += 17;
- next = strchrnul(found, '\n');
- sigc->signer = xmemdupz(found, next - found);
- }
- }
-}
-
-void check_commit_signature(const struct commit* commit, struct signature_check *sigc)
+void check_commit_signature(const struct commit *commit, struct signature_check *sigc)
{
struct strbuf payload = STRBUF_INIT;
struct strbuf signature = STRBUF_INIT;
}
}
+const char *find_commit_header(const char *msg, const char *key, size_t *out_len)
+{
+ int key_len = strlen(key);
+ const char *line = msg;
+
+ while (line) {
+ const char *eol = strchrnul(line, '\n');
+
+ if (line == eol)
+ return NULL;
+
+ if (eol - line > key_len &&
+ !strncmp(line, key, key_len) &&
+ line[key_len] == ' ') {
+ *out_len = eol - line - key_len - 1;
+ return line + key_len + 1;
+ }
+ line = *eol ? eol + 1 : NULL;
+ }
+ return NULL;
+}
++
+ /*
+ * Inspect sb and determine the true "end" of the log message, in
+ * order to find where to put a new Signed-off-by: line. Ignored are
+ * trailing comment lines and blank lines, and also the traditional
+ * "Conflicts:" block that is not commented out, so that we can use
+ * "git commit -s --amend" on an existing commit that forgot to remove
+ * it.
+ *
+ * Returns the number of bytes from the tail to ignore, to be fed as
+ * the second parameter to append_signoff().
+ */
+ int ignore_non_trailer(struct strbuf *sb)
+ {
+ int boc = 0;
+ int bol = 0;
+ int in_old_conflicts_block = 0;
+
+ while (bol < sb->len) {
+ char *next_line;
+
+ if (!(next_line = memchr(sb->buf + bol, '\n', sb->len - bol)))
+ next_line = sb->buf + sb->len;
+ else
+ next_line++;
+
+ if (sb->buf[bol] == comment_line_char || sb->buf[bol] == '\n') {
+ /* is this the first of the run of comments? */
+ if (!boc)
+ boc = bol;
+ /* otherwise, it is just continuing */
+ } else if (starts_with(sb->buf + bol, "Conflicts:\n")) {
+ in_old_conflicts_block = 1;
+ if (!boc)
+ boc = bol;
+ } else if (in_old_conflicts_block && sb->buf[bol] == '\t') {
+ ; /* a pathname in the conflicts block */
+ } else if (boc) {
+ /* the previous was not trailing comment */
+ boc = 0;
+ in_old_conflicts_block = 0;
+ }
+ bol = next_line - sb->buf;
+ }
+ return boc ? sb->len - boc : 0;
+ }
extern void free_commit_extra_headers(struct commit_extra_header *extra);
+/*
+ * Search the commit object contents given by "msg" for the header "key".
+ * Returns a pointer to the start of the header contents, or NULL. The length
+ * of the header, up to the first newline, is returned via out_len.
+ *
+ * Note that some headers (like mergetag) may be multi-line. It is the caller's
+ * responsibility to parse further in this case!
+ */
+extern const char *find_commit_header(const char *msg, const char *key,
+ size_t *out_len);
+
+ /* Find the end of the log message, the right place for a new trailer. */
+ extern int ignore_non_trailer(struct strbuf *sb);
+
typedef void (*each_mergetag_fn)(struct commit *commit, struct commit_extra_header *extra,
void *cb_data);
* at all. This may allocate memory for sig->gpg_output, sig->gpg_status,
* sig->signer and sig->key.
*/
-extern void check_commit_signature(const struct commit* commit, struct signature_check *sigc);
+extern void check_commit_signature(const struct commit *commit, struct signature_check *sigc);
int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
#include "cache.h"
+#include "lockfile.h"
#include "sequencer.h"
#include "dir.h"
#include "object.h"
if (!transaction ||
ref_transaction_update(transaction, "HEAD",
to, unborn ? null_sha1 : from,
- 0, 1, &err) ||
- ref_transaction_commit(transaction, sb.buf, &err)) {
+ 0, 1, sb.buf, &err) ||
+ ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
error("%s", err.buf);
strbuf_release(&sb);
return 0;
}
+ void append_conflicts_hint(struct strbuf *msgbuf)
+ {
+ int i;
+
+ strbuf_addch(msgbuf, '\n');
+ strbuf_commented_addf(msgbuf, "Conflicts:\n");
+ for (i = 0; i < active_nr;) {
+ const struct cache_entry *ce = active_cache[i++];
+ if (ce_stage(ce)) {
+ strbuf_commented_addf(msgbuf, "\t%s\n", ce->name);
+ while (i < active_nr && !strcmp(ce->name,
+ active_cache[i]->name))
+ i++;
+ }
+ }
+ }
+
static int do_recursive_merge(struct commit *base, struct commit *next,
const char *base_label, const char *next_label,
unsigned char *head, struct strbuf *msgbuf,
if (opts->signoff)
append_signoff(msgbuf, 0, 0);
- if (!clean) {
- int i;
- strbuf_addstr(msgbuf, "\nConflicts:\n");
- for (i = 0; i < active_nr;) {
- const struct cache_entry *ce = active_cache[i++];
- if (ce_stage(ce)) {
- strbuf_addch(msgbuf, '\t');
- strbuf_addstr(msgbuf, ce->name);
- strbuf_addch(msgbuf, '\n');
- while (i < active_nr && !strcmp(ce->name,
- active_cache[i]->name))
- i++;
- }
- }
- }
+ if (!clean)
+ append_conflicts_hint(msgbuf);
return !clean;
}
unsigned char head_sha1[20];
struct commit *head_commit;
- if (!resolve_ref_unsafe("HEAD", head_sha1, 1, NULL))
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
return error(_("Could not resolve HEAD commit\n"));
head_commit = lookup_commit(head_sha1);
if (!file_exists(git_path("CHERRY_PICK_HEAD")) &&
!file_exists(git_path("REVERT_HEAD")))
return error(_("no cherry-pick or revert in progress"));
- if (read_ref_full("HEAD", head_sha1, 0, NULL))
+ if (read_ref_full("HEAD", 0, head_sha1, NULL))
return error(_("cannot resolve HEAD"));
if (is_null_sha1(head_sha1))
return error(_("cannot abort from a branch yet to be born"));
#include "string-list.h"
#include "run-command.h"
#include "string-list.h"
+ #include "commit.h"
#include "trailer.h"
/*
* Copyright (c) 2013, 2014 Christian Couder <chriscool@tuxfamily.org>
{
struct strbuf cmd = STRBUF_INIT;
struct strbuf buf = STRBUF_INIT;
- struct child_process cp;
+ struct child_process cp = CHILD_PROCESS_INIT;
const char *argv[] = {NULL, NULL};
const char *result;
strbuf_replace(&cmd, TRAILER_ARG_STRING, arg);
argv[0] = cmd.buf;
- memset(&cp, 0, sizeof(cp));
cp.argv = argv;
cp.env = local_repo_env;
cp.no_stdin = 1;
return only_spaces ? count : 0;
}
+ /* Get the index of the end of the trailers */
+ static int find_trailer_end(struct strbuf **lines, int patch_start)
+ {
+ struct strbuf sb = STRBUF_INIT;
+ int i, ignore_bytes;
+
+ for (i = 0; i < patch_start; i++)
+ strbuf_addbuf(&sb, lines[i]);
+ ignore_bytes = ignore_non_trailer(&sb);
+ strbuf_release(&sb);
+ for (i = patch_start - 1; i >= 0 && ignore_bytes > 0; i--)
+ ignore_bytes -= lines[i]->len;
+
+ return i + 1;
+ }
+
static int has_blank_line_before(struct strbuf **lines, int start)
{
for (;start >= 0; start--) {
struct trailer_item **in_tok_last)
{
int count = 0;
- int patch_start, trailer_start, i;
+ int patch_start, trailer_start, trailer_end, i;
/* Get the line count */
while (lines[count])
count++;
patch_start = find_patch_start(lines, count);
- trailer_start = find_trailer_start(lines, patch_start);
+ trailer_end = find_trailer_end(lines, patch_start);
+ trailer_start = find_trailer_start(lines, trailer_end);
/* Print lines before the trailers as is */
print_lines(lines, 0, trailer_start);
printf("\n");
/* Parse trailer lines */
- for (i = trailer_start; i < patch_start; i++) {
+ for (i = trailer_start; i < trailer_end; i++) {
if (lines[i]->buf[0] != comment_line_char) {
struct trailer_item *new = create_trailer_item(lines[i]->buf);
add_trailer_item(in_tok_first, in_tok_last, new);
}
}
- return patch_start;
+ return trailer_end;
}
static void free_all(struct trailer_item **first)
struct trailer_item *in_tok_last = NULL;
struct trailer_item *arg_tok_first;
struct strbuf **lines;
- int patch_start;
+ int trailer_end;
/* Default config must be setup first */
git_config(git_trailer_default_config, NULL);
lines = read_input_file(file);
/* Print the lines before the trailers */
- patch_start = process_input_file(lines, &in_tok_first, &in_tok_last);
+ trailer_end = process_input_file(lines, &in_tok_first, &in_tok_last);
arg_tok_first = process_command_line_args(trailers);
free_all(&in_tok_first);
/* Print the lines after the trailers as is */
- print_lines(lines, patch_start, INT_MAX);
+ print_lines(lines, trailer_end, INT_MAX);
strbuf_list_free(lines);
}