--------
[verse]
'git' [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]
- [-p|--paginate|--no-pager]
+ [-p|--paginate|--no-pager] [--no-replace-objects]
[--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]
[--help] COMMAND [ARGS]
branch of the `git.git` repository.
Documentation for older releases are available here:
- * link:v1.6.5.6/git.html[documentation for release 1.6.5.6]
+ * link:v1.6.5.7/git.html[documentation for release 1.6.5.7]
* release notes for
+ link:RelNotes-1.6.5.7.txt[1.6.5.7],
link:RelNotes-1.6.5.6.txt[1.6.5.6],
link:RelNotes-1.6.5.5.txt[1.6.5.5],
link:RelNotes-1.6.5.4.txt[1.6.5.4],
environment is not set, it is set to the current working
directory.
+--no-replace-objects::
+ Do not use replacement refs to replace git objects. See
+ linkgit:git-replace[1] for more information.
+
FURTHER DOCUMENTATION
---------------------
return BRANCH_COLOR_LOCAL;
if (!strcasecmp(var+ofs, "current"))
return BRANCH_COLOR_CURRENT;
- die("bad config variable '%s'", var);
+ return -1;
}
static int git_branch_config(const char *var, const char *value, void *cb)
}
if (!prefixcmp(var, "color.branch.")) {
int slot = parse_branch_color_slot(var, 13);
+ if (slot < 0)
+ return 0;
if (!value)
return config_error_nonbool(var);
color_parse(value, var, branch_colors[slot]);
commit = item->commit;
if (commit && !parse_commit(commit)) {
+ struct pretty_print_context ctx = {0};
pretty_print_commit(CMIT_FMT_ONELINE, commit,
- &subject, 0, NULL, NULL, 0, 0);
+ &subject, &ctx);
sub = subject.buf;
}
static char *edit_message, *use_message;
static char *author_name, *author_email, *author_date;
static int all, edit_flag, also, interactive, only, amend, signoff;
-static int quiet, verbose, no_verify, allow_empty, dry_run;
+static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
static char *untracked_files_arg;
/*
* The default commit message cleanup mode will remove the lines
OPT_FILENAME('F', "file", &logfile, "read log from file"),
OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
- OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "),
+ OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit"),
OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
+ OPT_BOOLEAN(0, "reset-author", &renew_authorship, "the commit is authored by me now (used with -C-c/--amend)"),
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_FILENAME('t', "template", &template_file, "use specified template file"),
OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
email = getenv("GIT_AUTHOR_EMAIL");
date = getenv("GIT_AUTHOR_DATE");
- if (use_message) {
+ if (use_message && !renew_authorship) {
const char *a, *lb, *rb, *eol;
a = strstr(use_message_buffer, "\nauthor ");
author_date = date;
}
+static int ends_rfc2822_footer(struct strbuf *sb)
+{
+ int ch;
+ int hit = 0;
+ int i, j, k;
+ int len = sb->len;
+ int first = 1;
+ const char *buf = sb->buf;
+
+ for (i = len - 1; i > 0; i--) {
+ if (hit && buf[i] == '\n')
+ break;
+ hit = (buf[i] == '\n');
+ }
+
+ while (i < len - 1 && buf[i] == '\n')
+ i++;
+
+ for (; i < len; i = k) {
+ for (k = i; k < len && buf[k] != '\n'; k++)
+ ; /* do nothing */
+ k++;
+
+ if ((buf[k] == ' ' || buf[k] == '\t') && !first)
+ continue;
+
+ first = 0;
+
+ for (j = 0; i + j < len; j++) {
+ ch = buf[i + j];
+ if (ch == ':')
+ break;
+ if (isalnum(ch) ||
+ (ch == '-'))
+ continue;
+ return 0;
+ }
+ }
+ return 1;
+}
+
static int prepare_to_commit(const char *index_file, const char *prefix,
struct wt_status *s)
{
for (i = sb.len - 1; i > 0 && sb.buf[i - 1] != '\n'; i--)
; /* do nothing */
if (prefixcmp(sb.buf + i, sob.buf)) {
- if (prefixcmp(sb.buf + i, sign_off_header))
+ if (!i || !ends_rfc2822_footer(&sb))
strbuf_addch(&sb, '\n');
strbuf_addbuf(&sb, &sob);
}
prepare_revision_walk(&revs);
commit = get_revision(&revs);
if (commit) {
+ struct pretty_print_context ctx = {0};
+ ctx.date_mode = DATE_NORMAL;
strbuf_release(&buf);
- format_commit_message(commit, "%an <%ae>", &buf, DATE_NORMAL);
+ format_commit_message(commit, "%an <%ae>", &buf, &ctx);
return strbuf_detach(&buf, NULL);
}
die("No existing author found with '%s'", name);
if (force_author && !strchr(force_author, '>'))
force_author = find_author_by_nickname(force_author);
+ if (force_author && renew_authorship)
+ die("Using both --reset-author and --author does not make sense");
+
if (logfile || message.len || use_message)
use_editor = 0;
if (edit_flag)
use_message = edit_message;
if (amend && !use_message)
use_message = "HEAD";
+ if (!use_message && renew_authorship)
+ die("--reset-author can be used only with -C, -c or --amend.");
if (use_message) {
unsigned char sha1[20];
static char utf8[] = "UTF-8";
return WT_STATUS_NOBRANCH;
if (!strcasecmp(var+offset, "unmerged"))
return WT_STATUS_UNMERGED;
- die("bad config variable '%s'", var);
+ return -1;
}
static int git_status_config(const char *k, const char *v, void *cb)
}
if (!prefixcmp(k, "status.color.") || !prefixcmp(k, "color.status.")) {
int slot = parse_status_slot(k, 13);
+ if (slot < 0)
+ return 0;
if (!v)
return config_error_nonbool(k);
color_parse(v, k, s->color_palette[slot]);
initial_commit ? " (root-commit)" : "");
if (!log_tree_commit(&rev, commit)) {
+ struct pretty_print_context ctx = {0};
struct strbuf buf = STRBUF_INIT;
- format_commit_message(commit, format + 7, &buf, DATE_NORMAL);
+ ctx.date_mode = DATE_NORMAL;
+ format_commit_message(commit, format + 7, &buf, &ctx);
printf("%s\n", buf.buf);
strbuf_release(&buf);
}
#include "utf8.h"
#include "userdiff.h"
#include "sigchain.h"
+#include "submodule.h"
#ifdef NO_FAST_WORKING_DIRECTORY
#define FAST_WORKING_DIRECTORY 0
GIT_COLOR_GREEN, /* NEW */
GIT_COLOR_YELLOW, /* COMMIT */
GIT_COLOR_BG_RED, /* WHITESPACE */
+ GIT_COLOR_NORMAL, /* FUNCINFO */
};
static void diff_filespec_load_driver(struct diff_filespec *one);
return DIFF_COMMIT;
if (!strcasecmp(var+ofs, "whitespace"))
return DIFF_WHITESPACE;
- die("bad config variable '%s'", var);
+ if (!strcasecmp(var+ofs, "func"))
+ return DIFF_FUNCINFO;
+ return -1;
}
static int git_config_rename(const char *var, const char *value)
if (!prefixcmp(var, "diff.color.") || !prefixcmp(var, "color.diff.")) {
int slot = parse_diff_color_slot(var, 11);
+ if (slot < 0)
+ return 0;
if (!value)
return config_error_nonbool(var);
color_parse(value, var, diff_colors[slot]);
nofirst = 0;
}
- fputs(set, file);
-
- if (!nofirst)
- fputc(first, file);
- fwrite(line, len, 1, file);
- fputs(reset, file);
+ if (len || !nofirst) {
+ fputs(set, file);
+ if (!nofirst)
+ fputc(first, file);
+ fwrite(line, len, 1, file);
+ fputs(reset, file);
+ }
if (has_trailing_carriage_return)
fputc('\r', file);
if (has_trailing_newline)
}
}
+static void emit_hunk_header(struct emit_callback *ecbdata,
+ const char *line, int len)
+{
+ const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
+ const char *frag = diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO);
+ const char *func = diff_get_color(ecbdata->color_diff, DIFF_FUNCINFO);
+ const char *reset = diff_get_color(ecbdata->color_diff, DIFF_RESET);
+ static const char atat[2] = { '@', '@' };
+ const char *cp, *ep;
+
+ /*
+ * As a hunk header must begin with "@@ -<old>, +<new> @@",
+ * it always is at least 10 bytes long.
+ */
+ if (len < 10 ||
+ memcmp(line, atat, 2) ||
+ !(ep = memmem(line + 2, len - 2, atat, 2))) {
+ emit_line(ecbdata->file, plain, reset, line, len);
+ return;
+ }
+ ep += 2; /* skip over @@ */
+
+ /* The hunk header in fraginfo color */
+ emit_line(ecbdata->file, frag, reset, line, ep - line);
+
+ /* blank before the func header */
+ for (cp = ep; ep - line < len; ep++)
+ if (*ep != ' ' && *ep != '\t')
+ break;
+ if (ep != cp)
+ emit_line(ecbdata->file, plain, reset, cp, ep - cp);
+
+ if (ep < line + len)
+ emit_line(ecbdata->file, func, reset, ep, line + len - ep);
+}
+
static struct diff_tempfile *claim_diff_tempfile(void) {
int i;
for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
diff_words_flush(ecbdata);
len = sane_truncate_line(ecbdata, line, len);
find_lno(line, ecbdata);
- emit_line(ecbdata->file,
- diff_get_color(ecbdata->color_diff, DIFF_FRAGINFO),
- reset, line, len);
+ emit_hunk_header(ecbdata, line, len);
if (line[len-1] != '\n')
putc('\n', ecbdata->file);
return;
total_files, adds, dels);
}
-static void show_shortstats(struct diffstat_t* data, struct diff_options *options)
+static void show_shortstats(struct diffstat_t *data, struct diff_options *options)
{
int i, adds = 0, dels = 0, total_files = data->nr;
const char *a_prefix, *b_prefix;
const char *textconv_one = NULL, *textconv_two = NULL;
+ if (DIFF_OPT_TST(o, SUBMODULE_LOG) &&
+ (!one->mode || S_ISGITLINK(one->mode)) &&
+ (!two->mode || S_ISGITLINK(two->mode))) {
+ const char *del = diff_get_color_opt(o, DIFF_FILE_OLD);
+ const char *add = diff_get_color_opt(o, DIFF_FILE_NEW);
+ show_submodule_summary(o->file, one ? one->path : two->path,
+ one->sha1, two->sha1,
+ del, add, reset);
+ return;
+ }
+
if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) {
textconv_one = get_textconv(one);
textconv_two = get_textconv(two);
DIFF_OPT_CLR(options, ALLOW_TEXTCONV);
else if (!strcmp(arg, "--ignore-submodules"))
DIFF_OPT_SET(options, IGNORE_SUBMODULES);
+ else if (!strcmp(arg, "--submodule"))
+ DIFF_OPT_SET(options, SUBMODULE_LOG);
+ else if (!prefixcmp(arg, "--submodule=")) {
+ if (!strcmp(arg + 12, "log"))
+ DIFF_OPT_SET(options, SUBMODULE_LOG);
+ }
/* misc options */
else if (!strcmp(arg, "-z"))
if (normalize_path_copy(sanitized, sanitized))
goto error_out;
if (is_absolute_path(orig)) {
+ size_t len, total;
const char *work_tree = get_git_work_tree();
- size_t len = strlen(work_tree);
- size_t total = strlen(sanitized) + 1;
+ if (!work_tree)
+ goto error_out;
+ len = strlen(work_tree);
+ total = strlen(sanitized) + 1;
if (strncmp(sanitized, work_tree, len) ||
(sanitized[len] != '\0' && sanitized[len] != '/')) {
error_out:
return path;
}
+int check_filename(const char *prefix, const char *arg)
+{
+ const char *name;
+ struct stat st;
+
+ name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
+ if (!lstat(name, &st))
+ return 1; /* file exists */
+ if (errno == ENOENT || errno == ENOTDIR)
+ return 0; /* file does not exist */
+ die_errno("failed to stat '%s'", arg);
+}
+
/*
* Verify a filename that we got as an argument for a pathspec
* entry. Note that a filename that begins with "-" never verifies
*/
void verify_filename(const char *prefix, const char *arg)
{
- const char *name;
- struct stat st;
-
if (*arg == '-')
die("bad flag '%s' used after filename", arg);
- name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
- if (!lstat(name, &st))
+ if (check_filename(prefix, arg))
return;
- if (errno == ENOENT)
- die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
- "Use '--' to separate paths from revisions", arg);
- die_errno("failed to stat '%s'", arg);
+ die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
+ "Use '--' to separate paths from revisions", arg);
}
/*
*/
void verify_non_filename(const char *prefix, const char *arg)
{
- const char *name;
- struct stat st;
-
if (!is_inside_work_tree() || is_inside_git_dir())
return;
if (*arg == '-')
return; /* flag */
- name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
- if (!lstat(name, &st))
- die("ambiguous argument '%s': both revision and filename\n"
- "Use '--' to separate filenames from revisions", arg);
- if (errno != ENOENT && errno != ENOTDIR)
- die_errno("failed to stat '%s'", arg);
+ if (!check_filename(prefix, arg))
+ return;
+ die("ambiguous argument '%s': both revision and filename\n"
+ "Use '--' to separate filenames from revisions", arg);
}
const char **get_pathspec(const char *prefix, const char **pathspec)