summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes-1.6.6.txt60
-rw-r--r--Documentation/config.txt6
-rw-r--r--Documentation/git-check-ref-format.txt9
-rw-r--r--Documentation/git-rebase.txt9
-rw-r--r--Documentation/git-replace.txt21
-rw-r--r--Documentation/git.txt6
-rw-r--r--Documentation/pt_BR/gittutorial.txt22
-rwxr-xr-xGIT-VERSION-GEN2
l---------RelNotes2
-rw-r--r--builtin-apply.c61
-rw-r--r--builtin-describe.c13
-rw-r--r--builtin-fetch.c107
-rw-r--r--builtin-ls-files.c8
-rw-r--r--cache.h4
-rw-r--r--command-list.txt1
-rw-r--r--commit.h2
-rwxr-xr-xcontrib/completion/git-completion.bash3
-rw-r--r--contrib/emacs/git-blame.el156
-rw-r--r--diff.c409
-rwxr-xr-xgit-add--interactive.perl2
-rwxr-xr-xgit-instaweb.sh16
-rwxr-xr-xgit-rebase--interactive.sh9
-rwxr-xr-xgit-send-email.perl6
-rw-r--r--git.c4
-rw-r--r--gitweb/gitweb.css6
-rwxr-xr-xgitweb/gitweb.perl78
-rw-r--r--log-tree.c1
-rw-r--r--pretty.c2
-rw-r--r--t/lib-rebase.sh6
-rwxr-xr-xt/t3003-ls-files-exclude.sh32
-rwxr-xr-xt/t3404-rebase-interactive.sh14
-rwxr-xr-xt/t3701-add-interactive.sh14
-rw-r--r--t/t4013/diff.log_--decorate=full_--all2
-rw-r--r--t/t4013/diff.log_--decorate_--all2
-rwxr-xr-xt/t4015-diff-whitespace.sh11
-rwxr-xr-xt/t4019-diff-wserror.sh11
-rwxr-xr-xt/t4124-apply-ws-rule.sh91
-rwxr-xr-xt/t6050-replace.sh7
-rwxr-xr-xt/t9001-send-email.sh94
-rw-r--r--ws.c19
40 files changed, 977 insertions, 351 deletions
diff --git a/Documentation/RelNotes-1.6.6.txt b/Documentation/RelNotes-1.6.6.txt
new file mode 100644
index 0000000000..5f1fecb5ec
--- /dev/null
+++ b/Documentation/RelNotes-1.6.6.txt
@@ -0,0 +1,60 @@
+GIT v1.6.6 Release Notes
+========================
+
+In git 1.7.0, which is planned to be the release after 1.6.6, "git
+push" into a branch that is currently checked out will be refused by
+default.
+
+You can choose what should happen upon such a push by setting the
+configuration variable receive.denyCurrentBranch in the receiving
+repository.
+
+Also, "git push $there :$killed" to delete the branch $killed in a remote
+repository $there, when $killed branch is the current branch pointed at by
+its HEAD, will be refused by default.
+
+You can choose what should happen upon such a push by setting the
+configuration variable receive.denyDeleteCurrent in the receiving
+repository.
+
+To ease the transition plan, the receiving repository of such a
+push running this release will issue a big warning when the
+configuration variable is missing. Please refer to:
+
+ http://git.or.cz/gitwiki/GitFaq#non-bare
+ http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
+
+for more details on the reason why this change is needed and the
+transition plan.
+
+Updates since v1.6.5
+--------------------
+
+(subsystems)
+
+(portability)
+
+(performance)
+
+(usability, bells and whistles)
+
+ * "git log --decorate" shows the location of HEAD as well.
+
+(developers)
+
+Fixes since v1.6.5
+------------------
+
+All of the fixes in v1.6.5.X maintenance series are included in this
+release, unless otherwise noted.
+
+ * "git apply" and "git diff" (including patch output from "git log -p")
+ now flags trailing blank lines as whitespace errors correctly (only
+ "apply --whitespace=fix" stripped them but "apply --whitespace=warn"
+ did not even warn).
+
+ * Two whitespace error classes, 'blank-at-eof' and 'blank-at-eol', have
+ been introduced (settable by core.whitespace configuration variable and
+ whitespace attribute). The 'trailing-space' whitespace error class has
+ become a short-hand to cover both of these and there is no behaviour
+ change for existing set-ups.
diff --git a/Documentation/config.txt b/Documentation/config.txt
index cd1781498e..5a8f57cbf7 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -416,13 +416,17 @@ core.whitespace::
consider them as errors. You can prefix `-` to disable
any of them (e.g. `-trailing-space`):
+
-* `trailing-space` treats trailing whitespaces at the end of the line
+* `blank-at-eol` treats trailing whitespaces at the end of the line
as an error (enabled by default).
* `space-before-tab` treats a space character that appears immediately
before a tab character in the initial indent part of the line as an
error (enabled by default).
* `indent-with-non-tab` treats a line that is indented with 8 or more
space characters as an error (not enabled by default).
+* `blank-at-eof` treats blank lines added at the end of file as an error
+ (enabled by default).
+* `trailing-space` is a short-hand to cover both `blank-at-eol` and
+ `blank-at-eof`.
* `cr-at-eol` treats a carriage-return at the end of line as
part of the line terminator, i.e. with it, `trailing-space`
does not trigger if the character before such a carriage-return
diff --git a/Documentation/git-check-ref-format.txt b/Documentation/git-check-ref-format.txt
index 0b7982ea76..e9b3b40af4 100644
--- a/Documentation/git-check-ref-format.txt
+++ b/Documentation/git-check-ref-format.txt
@@ -9,7 +9,7 @@ SYNOPSIS
--------
[verse]
'git check-ref-format' <refname>
-'git check-ref-format' [--branch] <branchname-shorthand>
+'git check-ref-format' --branch <branchname-shorthand>
DESCRIPTION
-----------
@@ -63,8 +63,11 @@ reference name expressions (see linkgit:git-rev-parse[1]):
. at-open-brace `@{` is used as a notation to access a reflog entry.
-With the `--branch` option, it expands a branch name shorthand and
-prints the name of the branch the shorthand refers to.
+With the `--branch` option, it expands the ``previous branch syntax''
+`@{-n}`. For example, `@{-1}` is a way to refer the last branch you
+were on. This option should be used by porcelains to accept this
+syntax anywhere a branch name is expected, so they can act as if you
+typed the branch name.
EXAMPLE
-------
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 0aefc34d0d..33e0ef1f6d 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -368,14 +368,17 @@ By replacing the command "pick" with the command "edit", you can tell
the files and/or the commit message, amend the commit, and continue
rebasing.
+If you just want to edit the commit message for a commit, replace the
+command "pick" with the command "reword".
+
If you want to fold two or more commits into one, replace the command
"pick" with "squash" for the second and subsequent commit. If the
commits had different authors, it will attribute the squashed commit to
the author of the first commit.
-In both cases, or when a "pick" does not succeed (because of merge
-errors), the loop will stop to let you fix things, and you can continue
-the loop with `git rebase --continue`.
+'git-rebase' will stop when "pick" has been replaced with "edit" or
+when a command fails due to merge errors. When you are done editing
+and/or resolving conflicts you can continue with `git rebase --continue`.
For example, if you want to reorder the last 5 commits, such that what
was HEAD~4 becomes the new HEAD. To achieve that, you would call
diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt
index 915cb77b29..8adc1ef55c 100644
--- a/Documentation/git-replace.txt
+++ b/Documentation/git-replace.txt
@@ -23,6 +23,26 @@ replacement object.
Unless `-f` is given, the replace reference must not yet exist in
`.git/refs/replace/` directory.
+Replace references will be used by default by all git commands except
+those doing reachability traversal (prune, pack transfer and fsck).
+
+It is possible to disable use of replacement refs for any command
+using the --no-replace-objects option just after "git".
+
+For example if commit "foo" has been replaced by commit "bar":
+
+------------------------------------------------
+$ git --no-replace-object cat-file commit foo
+------------------------------------------------
+
+show information about commit "foo", while:
+
+------------------------------------------------
+$ git cat-file commit foo
+------------------------------------------------
+
+show information about commit "bar".
+
OPTIONS
-------
-f::
@@ -54,6 +74,7 @@ SEE ALSO
--------
linkgit:git-tag[1]
linkgit:git-branch[1]
+linkgit:git[1]
Author
------
diff --git a/Documentation/git.txt b/Documentation/git.txt
index d11c5c1651..df46a34371 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -10,7 +10,7 @@ SYNOPSIS
--------
[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]
@@ -238,6 +238,10 @@ help ...`.
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
---------------------
diff --git a/Documentation/pt_BR/gittutorial.txt b/Documentation/pt_BR/gittutorial.txt
index 81e7ad7df4..beba065252 100644
--- a/Documentation/pt_BR/gittutorial.txt
+++ b/Documentation/pt_BR/gittutorial.txt
@@ -1,15 +1,15 @@
gittutorial(7)
==============
-NAME
+NOME
----
gittutorial - Um tutorial de introdução ao git (para versão 1.5.1 ou mais nova)
-SYNOPSIS
+SINOPSE
--------
git *
-DESCRIPTION
+DESCRIÇÃO
-----------
Este tutorial explica como importar um novo projeto para o git,
@@ -64,11 +64,11 @@ Git irá responder
Initialized empty Git repository in .git/
------------------------------------------------
-Você agora iniciou seu diretório de trabalho--você deve ter notado um
-novo diretório criado, com o nome de ".git".
+Agora que você iniciou seu diretório de trabalho, você deve ter notado que um
+novo diretório foi criado com o nome de ".git".
A seguir, diga ao git para gravar um instantâneo do conteúdo de todos os
-arquivos sob o diretório corrente (note o '.'), com 'git-add':
+arquivos sob o diretório atual (note o '.'), com 'git-add':
------------------------------------------------
$ git add .
@@ -126,8 +126,8 @@ mudanças com:
$ git commit
------------------------------------------------
-Isto irá novamente te pedir por uma mensagem descrevendo a mudança, e,
-então, gravar a nova versão do projeto.
+Ao executar esse comando, ele irá te pedir uma mensagem descrevendo a mudança,
+e, então, irá gravar a nova versão do projeto.
Alternativamente, ao invés de executar 'git-add' antes, você pode usar
@@ -143,7 +143,7 @@ idéia começar a mensagem com uma simples e curta (menos de 50
caracteres) linha sumarizando a mudança, seguida de uma linha em branco
e, então, uma descrição mais detalhada. Ferramentas que transformam
commits em email, por exemplo, usam a primeira linha no campo de
-cabeçalho Subject: e o resto no corpo.
+cabeçalho "Subject:" e o resto no corpo.
Git rastreia conteúdo, não arquivos
----------------------------
@@ -155,7 +155,7 @@ usado tanto para arquivos novos e arquivos recentemente modificados, e
em ambos os casos, ele tira o instantâneo dos arquivos dados e armazena
o conteúdo no índice, pronto para inclusão do próximo commit.
-Visualizando história do projeto
+Visualizando a história do projeto
-----------------------
Em qualquer ponto você pode visualizar a história das suas mudanças
@@ -165,7 +165,7 @@ usando
$ git log
------------------------------------------------
-Se você também quer ver a diferença completa a cada passo, use
+Se você também quiser ver a diferença completa a cada passo, use
------------------------------------------------
$ git log -p
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index d11e43cf31..710d361233 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.6.5.1
+DEF_VER=v1.6.5.GIT
LF='
'
diff --git a/RelNotes b/RelNotes
index dda06d1d8f..5274ceed4e 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.5.1.txt \ No newline at end of file
+Documentation/RelNotes-1.6.6.txt \ No newline at end of file
diff --git a/builtin-apply.c b/builtin-apply.c
index c8372a0a80..f667368d16 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -153,6 +153,7 @@ struct fragment {
const char *patch;
int size;
int rejected;
+ int linenr;
struct fragment *next;
};
@@ -1227,23 +1228,29 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
return -1;
}
-static void check_whitespace(const char *line, int len, unsigned ws_rule)
+static void record_ws_error(unsigned result, const char *line, int len, int linenr)
{
char *err;
- unsigned result = ws_check(line + 1, len - 1, ws_rule);
+
if (!result)
return;
whitespace_error++;
if (squelch_whitespace_errors &&
squelch_whitespace_errors < whitespace_error)
- ;
- else {
- err = whitespace_error_string(result);
- fprintf(stderr, "%s:%d: %s.\n%.*s\n",
- patch_input_file, linenr, err, len - 2, line + 1);
- free(err);
- }
+ return;
+
+ err = whitespace_error_string(result);
+ fprintf(stderr, "%s:%d: %s.\n%.*s\n",
+ patch_input_file, linenr, err, len, line);
+ free(err);
+}
+
+static void check_whitespace(const char *line, int len, unsigned ws_rule)
+{
+ unsigned result = ws_check(line + 1, len - 1, ws_rule);
+
+ record_ws_error(result, line + 1, len - 2, linenr);
}
/*
@@ -1359,6 +1366,7 @@ static int parse_single_patch(char *line, unsigned long size, struct patch *patc
int len;
fragment = xcalloc(1, sizeof(*fragment));
+ fragment->linenr = linenr;
len = parse_fragment(line, size, patch, fragment);
if (len <= 0)
die("corrupt patch at line %d", linenr);
@@ -2142,6 +2150,7 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
int len = linelen(patch, size);
int plen, added;
int added_blank_line = 0;
+ int is_blank_context = 0;
if (!len)
break;
@@ -2174,8 +2183,12 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
*new++ = '\n';
add_line_info(&preimage, "\n", 1, LINE_COMMON);
add_line_info(&postimage, "\n", 1, LINE_COMMON);
+ is_blank_context = 1;
break;
case ' ':
+ if (plen && (ws_rule & WS_BLANK_AT_EOF) &&
+ ws_blank_line(patch + 1, plen, ws_rule))
+ is_blank_context = 1;
case '-':
memcpy(old, patch + 1, plen);
add_line_info(&preimage, old, plen,
@@ -2202,7 +2215,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
(first == '+' ? 0 : LINE_COMMON));
new += added;
if (first == '+' &&
- added == 1 && new[-1] == '\n')
+ (ws_rule & WS_BLANK_AT_EOF) &&
+ ws_blank_line(patch + 1, plen, ws_rule))
added_blank_line = 1;
break;
case '@': case '\\':
@@ -2215,6 +2229,8 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
}
if (added_blank_line)
new_blank_lines_at_end++;
+ else if (is_blank_context)
+ ;
else
new_blank_lines_at_end = 0;
patch += len;
@@ -2296,17 +2312,24 @@ static int apply_one_fragment(struct image *img, struct fragment *frag,
}
if (applied_pos >= 0) {
- if (ws_error_action == correct_ws_error &&
- new_blank_lines_at_end &&
- postimage.nr + applied_pos == img->nr) {
+ if (new_blank_lines_at_end &&
+ preimage.nr + applied_pos == img->nr &&
+ (ws_rule & WS_BLANK_AT_EOF) &&
+ ws_error_action != nowarn_ws_error) {
+ record_ws_error(WS_BLANK_AT_EOF, "+", 1, frag->linenr);
+ if (ws_error_action == correct_ws_error) {
+ while (new_blank_lines_at_end--)
+ remove_last_line(&postimage);
+ }
/*
- * If the patch application adds blank lines
- * at the end, and if the patch applies at the
- * end of the image, remove those added blank
- * lines.
+ * We would want to prevent write_out_results()
+ * from taking place in apply_patch() that follows
+ * the callchain led us here, which is:
+ * apply_patch->check_patch_list->check_patch->
+ * apply_data->apply_fragments->apply_one_fragment
*/
- while (new_blank_lines_at_end--)
- remove_last_line(&postimage);
+ if (ws_error_action == die_on_ws_error)
+ apply = 0;
}
/*
diff --git a/builtin-describe.c b/builtin-describe.c
index df67a733ae..2dcfd3dfeb 100644
--- a/builtin-describe.c
+++ b/builtin-describe.c
@@ -180,7 +180,6 @@ static void describe(const char *arg, int last_one)
unsigned char sha1[20];
struct commit *cmit, *gave_up_on = NULL;
struct commit_list *list;
- static int initialized = 0;
struct commit_name *n;
struct possible_tag all_matches[MAX_TAGS];
unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
@@ -192,14 +191,6 @@ static void describe(const char *arg, int last_one)
if (!cmit)
die("%s is not a valid '%s' object", arg, commit_type);
- if (!initialized) {
- initialized = 1;
- for_each_ref(get_name, NULL);
- }
-
- if (!found_names)
- die("cannot describe '%s'", sha1_to_hex(sha1));
-
n = cmit->util;
if (n) {
/*
@@ -359,6 +350,10 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
return cmd_name_rev(i + argc, args, prefix);
}
+ for_each_ref(get_name, NULL);
+ if (!found_names)
+ die("No names found, cannot describe anything.");
+
if (argc == 0) {
describe("HEAD", 1);
} else {
diff --git a/builtin-fetch.c b/builtin-fetch.c
index cb48c57ca3..a35a6f8cb8 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -504,57 +504,98 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
return 0;
}
+struct tag_data {
+ struct ref **head;
+ struct ref ***tail;
+};
+
+static int add_to_tail(struct string_list_item *item, void *cb_data)
+{
+ struct tag_data *data = (struct tag_data *)cb_data;
+ struct ref *rm = NULL;
+
+ /* We have already decided to ignore this item */
+ if (!item->util)
+ return 0;
+
+ rm = alloc_ref(item->string);
+ rm->peer_ref = alloc_ref(item->string);
+ hashcpy(rm->old_sha1, item->util);
+
+ **data->tail = rm;
+ *data->tail = &rm->next;
+
+ return 0;
+}
+
static void find_non_local_tags(struct transport *transport,
struct ref **head,
struct ref ***tail)
{
struct string_list existing_refs = { NULL, 0, 0, 0 };
- struct string_list new_refs = { NULL, 0, 0, 1 };
- char *ref_name;
- int ref_name_len;
- const unsigned char *ref_sha1;
- const struct ref *tag_ref;
- struct ref *rm = NULL;
+ struct string_list remote_refs = { NULL, 0, 0, 0 };
+ struct tag_data data = {head, tail};
const struct ref *ref;
+ struct string_list_item *item = NULL;
for_each_ref(add_existing, &existing_refs);
for (ref = transport_get_remote_refs(transport); ref; ref = ref->next) {
if (prefixcmp(ref->name, "refs/tags"))
continue;
- ref_name = xstrdup(ref->name);
- ref_name_len = strlen(ref_name);
- ref_sha1 = ref->old_sha1;
-
- if (!strcmp(ref_name + ref_name_len - 3, "^{}")) {
- ref_name[ref_name_len - 3] = 0;
- tag_ref = transport_get_remote_refs(transport);
- while (tag_ref) {
- if (!strcmp(tag_ref->name, ref_name)) {
- ref_sha1 = tag_ref->old_sha1;
- break;
- }
- tag_ref = tag_ref->next;
- }
+ /*
+ * The peeled ref always follows the matching base
+ * ref, so if we see a peeled ref that we don't want
+ * to fetch then we can mark the ref entry in the list
+ * as one to ignore by setting util to NULL.
+ */
+ if (!strcmp(ref->name + strlen(ref->name) - 3, "^{}")) {
+ if (item && !has_sha1_file(ref->old_sha1) &&
+ !will_fetch(head, ref->old_sha1) &&
+ !has_sha1_file(item->util) &&
+ !will_fetch(head, item->util))
+ item->util = NULL;
+ item = NULL;
+ continue;
}
- if (!string_list_has_string(&existing_refs, ref_name) &&
- !string_list_has_string(&new_refs, ref_name) &&
- (has_sha1_file(ref->old_sha1) ||
- will_fetch(head, ref->old_sha1))) {
- string_list_insert(ref_name, &new_refs);
+ /*
+ * If item is non-NULL here, then we previously saw a
+ * ref not followed by a peeled reference, so we need
+ * to check if it is a lightweight tag that we want to
+ * fetch.
+ */
+ if (item && !has_sha1_file(item->util) &&
+ !will_fetch(head, item->util))
+ item->util = NULL;
- rm = alloc_ref(ref_name);
- rm->peer_ref = alloc_ref(ref_name);
- hashcpy(rm->old_sha1, ref_sha1);
+ item = NULL;
- **tail = rm;
- *tail = &rm->next;
- }
- free(ref_name);
+ /* skip duplicates and refs that we already have */
+ if (string_list_has_string(&remote_refs, ref->name) ||
+ string_list_has_string(&existing_refs, ref->name))
+ continue;
+
+ item = string_list_insert(ref->name, &remote_refs);
+ item->util = (void *)ref->old_sha1;
}
string_list_clear(&existing_refs, 0);
- string_list_clear(&new_refs, 0);
+
+ /*
+ * We may have a final lightweight tag that needs to be
+ * checked to see if it needs fetching.
+ */
+ if (item && !has_sha1_file(item->util) &&
+ !will_fetch(head, item->util))
+ item->util = NULL;
+
+ /*
+ * For all the tags in the remote_refs string list, call
+ * add_to_tail to add them to the list of refs to be fetched
+ */
+ for_each_string_list(add_to_tail, &remote_refs, &data);
+
+ string_list_clear(&remote_refs, 0);
}
static void check_not_current_branch(struct ref *ref_map)
diff --git a/builtin-ls-files.c b/builtin-ls-files.c
index 2c95ca6105..c5c0407b0b 100644
--- a/builtin-ls-files.c
+++ b/builtin-ls-files.c
@@ -170,10 +170,6 @@ static void show_files(struct dir_struct *dir, const char *prefix)
if (show_cached | show_stage) {
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
- int dtype = ce_to_dtype(ce);
- if (excluded(dir, ce->name, &dtype) !=
- !!(dir->flags & DIR_SHOW_IGNORED))
- continue;
if (show_unmerged && !ce_stage(ce))
continue;
if (ce->ce_flags & CE_UPDATE)
@@ -186,10 +182,6 @@ static void show_files(struct dir_struct *dir, const char *prefix)
struct cache_entry *ce = active_cache[i];
struct stat st;
int err;
- int dtype = ce_to_dtype(ce);
- if (excluded(dir, ce->name, &dtype) !=
- !!(dir->flags & DIR_SHOW_IGNORED))
- continue;
if (ce->ce_flags & CE_UPDATE)
continue;
err = lstat(ce->name, &st);
diff --git a/cache.h b/cache.h
index a5eeead1e2..96840c7af7 100644
--- a/cache.h
+++ b/cache.h
@@ -986,10 +986,12 @@ void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, i
* whitespace rules.
* used by both diff and apply
*/
-#define WS_TRAILING_SPACE 01
+#define WS_BLANK_AT_EOL 01
#define WS_SPACE_BEFORE_TAB 02
#define WS_INDENT_WITH_NON_TAB 04
#define WS_CR_AT_EOL 010
+#define WS_BLANK_AT_EOF 020
+#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)
#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB)
extern unsigned whitespace_rule_cfg;
extern unsigned whitespace_rule(const char *);
diff --git a/command-list.txt b/command-list.txt
index fb03a2ebb5..59b0adc39b 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -92,6 +92,7 @@ git-reflog ancillarymanipulators
git-relink ancillarymanipulators
git-remote ancillarymanipulators
git-repack ancillarymanipulators
+git-replace ancillarymanipulators
git-repo-config ancillarymanipulators deprecated
git-request-pull foreignscminterface
git-rerere ancillaryinterrogators
diff --git a/commit.h b/commit.h
index f4fc5c5589..95f981a1a9 100644
--- a/commit.h
+++ b/commit.h
@@ -70,7 +70,7 @@ extern char *reencode_commit_message(const struct commit *commit,
const char **encoding_p);
extern void get_commit_format(const char *arg, struct rev_info *);
extern void format_commit_message(const struct commit *commit,
- const void *format, struct strbuf *sb,
+ const char *format, struct strbuf *sb,
enum date_mode dmode);
extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,
struct strbuf *,
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 7cf8557468..d3fec32997 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1069,7 +1069,8 @@ _git_grep ()
return
;;
esac
- COMPREPLY=()
+
+ __gitcomp "$(__git_refs)"
}
_git_help ()
diff --git a/contrib/emacs/git-blame.el b/contrib/emacs/git-blame.el
index 4fa70c5ad4..7f4c792978 100644
--- a/contrib/emacs/git-blame.el
+++ b/contrib/emacs/git-blame.el
@@ -80,6 +80,57 @@
(eval-when-compile (require 'cl)) ; to use `push', `pop'
+(defface git-blame-prefix-face
+ '((((background dark)) (:foreground "gray"
+ :background "black"))
+ (((background light)) (:foreground "gray"
+ :background "white"))
+ (t (:weight bold)))
+ "The face used for the hash prefix."
+ :group 'git-blame)
+
+(defgroup git-blame nil
+ "A minor mode showing Git blame information."
+ :group 'git
+ :link '(function-link git-blame-mode))
+
+
+(defcustom git-blame-use-colors t
+ "Use colors to indicate commits in `git-blame-mode'."
+ :type 'boolean
+ :group 'git-blame)
+
+(defcustom git-blame-prefix-format
+ "%h %20A:"
+ "The format of the prefix added to each line in `git-blame'
+mode. The format is passed to `format-spec' with the following format keys:
+
+ %h - the abbreviated hash
+ %H - the full hash
+ %a - the author name
+ %A - the author email
+ %c - the committer name
+ %C - the committer email
+ %s - the commit summary
+"
+ :group 'git-blame)
+
+(defcustom git-blame-mouseover-format
+ "%h %a %A: %s"
+ "The format of the description shown when pointing at a line in
+`git-blame' mode. The format string is passed to `format-spec'
+with the following format keys:
+
+ %h - the abbreviated hash
+ %H - the full hash
+ %a - the author name
+ %A - the author email
+ %c - the committer name
+ %C - the committer email
+ %s - the commit summary
+"
+ :group 'git-blame)
+
(defun git-blame-color-scale (&rest elements)
"Given a list, returns a list of triples formed with each
@@ -302,72 +353,69 @@ See also function `git-blame-mode'."
(src-line (string-to-number (match-string 2)))
(res-line (string-to-number (match-string 3)))
(num-lines (string-to-number (match-string 4))))
- (setq git-blame-current
- (if (string= hash "0000000000000000000000000000000000000000")
- nil
- (git-blame-new-commit
- hash src-line res-line num-lines))))
- (delete-region (point) (match-end 0))
- t)
- ((looking-at "filename \\(.+\\)\n")
- (let ((filename (match-string 1)))
- (git-blame-add-info "filename" filename))
- (delete-region (point) (match-end 0))
+ (delete-region (point) (match-end 0))
+ (setq git-blame-current (list (git-blame-new-commit hash)
+ src-line res-line num-lines)))
t)
((looking-at "\\([a-z-]+\\) \\(.+\\)\n")
(let ((key (match-string 1))
(value (match-string 2)))
- (git-blame-add-info key value))
- (delete-region (point) (match-end 0))
- t)
- ((looking-at "boundary\n")
- (setq git-blame-current nil)
- (delete-region (point) (match-end 0))
+ (delete-region (point) (match-end 0))
+ (git-blame-add-info (car git-blame-current) key value)
+ (when (string= key "filename")
+ (git-blame-create-overlay (car git-blame-current)
+ (caddr git-blame-current)
+ (cadddr git-blame-current))
+ (setq git-blame-current nil)))
t)
(t
nil)))
-(defun git-blame-new-commit (hash src-line res-line num-lines)
+(defun git-blame-new-commit (hash)
+ (with-current-buffer git-blame-file
+ (or (gethash hash git-blame-cache)
+ ;; Assign a random color to each new commit info
+ ;; Take care not to select the same color multiple times
+ (let* ((color (if git-blame-colors
+ (git-blame-random-pop git-blame-colors)
+ git-blame-ancient-color))
+ (info `(,hash (color . ,color))))
+ (puthash hash info git-blame-cache)
+ info))))
+
+(defun git-blame-create-overlay (info start-line num-lines)
(save-excursion
(set-buffer git-blame-file)
- (let ((info (gethash hash git-blame-cache))
- (inhibit-point-motion-hooks t)
+ (let ((inhibit-point-motion-hooks t)
(inhibit-modification-hooks t))
- (when (not info)
- ;; Assign a random color to each new commit info
- ;; Take care not to select the same color multiple times
- (let ((color (if git-blame-colors
- (git-blame-random-pop git-blame-colors)
- git-blame-ancient-color)))
- (setq info (list hash src-line res-line num-lines
- (git-describe-commit hash)
- (cons 'color color))))
- (puthash hash info git-blame-cache))
- (goto-line res-line)
- (while (> num-lines 0)
- (if (get-text-property (point) 'git-blame)
- (forward-line)
- (let* ((start (point))
- (end (progn (forward-line 1) (point)))
- (ovl (make-overlay start end)))
- (push ovl git-blame-overlays)
- (overlay-put ovl 'git-blame info)
- (overlay-put ovl 'help-echo hash)
+ (goto-line start-line)
+ (let* ((start (point))
+ (end (progn (forward-line num-lines) (point)))
+ (ovl (make-overlay start end))
+ (hash (car info))
+ (spec `((?h . ,(substring hash 0 6))
+ (?H . ,hash)
+ (?a . ,(git-blame-get-info info 'author))
+ (?A . ,(git-blame-get-info info 'author-mail))
+ (?c . ,(git-blame-get-info info 'committer))
+ (?C . ,(git-blame-get-info info 'committer-mail))
+ (?s . ,(git-blame-get-info info 'summary)))))
+ (push ovl git-blame-overlays)
+ (overlay-put ovl 'git-blame info)
+ (overlay-put ovl 'help-echo
+ (format-spec git-blame-mouseover-format spec))
+ (if git-blame-use-colors
(overlay-put ovl 'face (list :background
- (cdr (assq 'color (nthcdr 5 info)))))
- ;; the point-entered property doesn't seem to work in overlays
- ;;(overlay-put ovl 'point-entered
- ;; `(lambda (x y) (git-blame-identify ,hash)))
- (let ((modified (buffer-modified-p)))
- (put-text-property (if (= start 1) start (1- start)) (1- end)
- 'point-entered
- `(lambda (x y) (git-blame-identify ,hash)))
- (set-buffer-modified-p modified))))
- (setq num-lines (1- num-lines))))))
-
-(defun git-blame-add-info (key value)
- (if git-blame-current
- (nconc git-blame-current (list (cons (intern key) value)))))
+ (cdr (assq 'color (cdr info))))))
+ (overlay-put ovl 'line-prefix
+ (propertize (format-spec git-blame-prefix-format spec)
+ 'face 'git-blame-prefix-face))))))
+
+(defun git-blame-add-info (info key value)
+ (nconc info (list (cons (intern key) value))))
+
+(defun git-blame-get-info (info key)
+ (cdr (assq key (cdr info))))
(defun git-blame-current-commit ()
(let ((info (get-char-property (point) 'git-blame)))
diff --git a/diff.c b/diff.c
index e1be189742..b0c7e616a6 100644
--- a/diff.c
+++ b/diff.c
@@ -174,6 +174,175 @@ static struct diff_tempfile {
char tmp_path[PATH_MAX];
} diff_temp[2];
+typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
+
+struct emit_callback {
+ int color_diff;
+ unsigned ws_rule;
+ int blank_at_eof_in_preimage;
+ int blank_at_eof_in_postimage;
+ int lno_in_preimage;
+ int lno_in_postimage;
+ sane_truncate_fn truncate;
+ const char **label_path;
+ struct diff_words_data *diff_words;
+ int *found_changesp;
+ FILE *file;
+};
+
+static int count_lines(const char *data, int size)
+{
+ int count, ch, completely_empty = 1, nl_just_seen = 0;
+ count = 0;
+ while (0 < size--) {
+ ch = *data++;
+ if (ch == '\n') {
+ count++;
+ nl_just_seen = 1;
+ completely_empty = 0;
+ }
+ else {
+ nl_just_seen = 0;
+ completely_empty = 0;
+ }
+ }
+ if (completely_empty)
+ return 0;
+ if (!nl_just_seen)
+ count++; /* no trailing newline */
+ return count;
+}
+
+static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
+{
+ if (!DIFF_FILE_VALID(one)) {
+ mf->ptr = (char *)""; /* does not matter */
+ mf->size = 0;
+ return 0;
+ }
+ else if (diff_populate_filespec(one, 0))
+ return -1;
+
+ mf->ptr = one->data;
+ mf->size = one->size;
+ return 0;
+}
+
+static int count_trailing_blank(mmfile_t *mf, unsigned ws_rule)
+{
+ char *ptr = mf->ptr;
+ long size = mf->size;
+ int cnt = 0;
+
+ if (!size)
+ return cnt;
+ ptr += size - 1; /* pointing at the very end */
+ if (*ptr != '\n')
+ ; /* incomplete line */
+ else
+ ptr--; /* skip the last LF */
+ while (mf->ptr < ptr) {
+ char *prev_eol;
+ for (prev_eol = ptr; mf->ptr <= prev_eol; prev_eol--)
+ if (*prev_eol == '\n')
+ break;
+ if (!ws_blank_line(prev_eol + 1, ptr - prev_eol, ws_rule))
+ break;
+ cnt++;
+ ptr = prev_eol - 1;
+ }
+ return cnt;
+}
+
+static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2,
+ struct emit_callback *ecbdata)
+{
+ int l1, l2, at;
+ unsigned ws_rule = ecbdata->ws_rule;
+ l1 = count_trailing_blank(mf1, ws_rule);
+ l2 = count_trailing_blank(mf2, ws_rule);
+ if (l2 <= l1) {
+ ecbdata->blank_at_eof_in_preimage = 0;
+ ecbdata->blank_at_eof_in_postimage = 0;
+ return;
+ }
+ at = count_lines(mf1->ptr, mf1->size);
+ ecbdata->blank_at_eof_in_preimage = (at - l1) + 1;
+
+ at = count_lines(mf2->ptr, mf2->size);
+ ecbdata->blank_at_eof_in_postimage = (at - l2) + 1;
+}
+
+static void emit_line_0(FILE *file, const char *set, const char *reset,
+ int first, const char *line, int len)
+{
+ int has_trailing_newline, has_trailing_carriage_return;
+ int nofirst;
+
+ if (len == 0) {
+ has_trailing_newline = (first == '\n');
+ has_trailing_carriage_return = (!has_trailing_newline &&
+ (first == '\r'));
+ nofirst = has_trailing_newline || has_trailing_carriage_return;
+ } else {
+ has_trailing_newline = (len > 0 && line[len-1] == '\n');
+ if (has_trailing_newline)
+ len--;
+ has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
+ if (has_trailing_carriage_return)
+ len--;
+ nofirst = 0;
+ }
+
+ 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)
+ fputc('\n', file);
+}
+
+static void emit_line(FILE *file, const char *set, const char *reset,
+ const char *line, int len)
+{
+ emit_line_0(file, set, reset, line[0], line+1, len-1);
+}
+
+static int new_blank_line_at_eof(struct emit_callback *ecbdata, const char *line, int len)
+{
+ if (!((ecbdata->ws_rule & WS_BLANK_AT_EOF) &&
+ ecbdata->blank_at_eof_in_preimage &&
+ ecbdata->blank_at_eof_in_postimage &&
+ ecbdata->blank_at_eof_in_preimage <= ecbdata->lno_in_preimage &&
+ ecbdata->blank_at_eof_in_postimage <= ecbdata->lno_in_postimage))
+ return 0;
+ return ws_blank_line(line, len, ecbdata->ws_rule);
+}
+
+static void emit_add_line(const char *reset,
+ struct emit_callback *ecbdata,
+ const char *line, int len)
+{
+ const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
+ const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
+
+ if (!*ws)
+ emit_line_0(ecbdata->file, set, reset, '+', line, len);
+ else if (new_blank_line_at_eof(ecbdata, line, len))
+ /* Blank line at EOF - paint '+' as well */
+ emit_line_0(ecbdata->file, ws, reset, '+', line, len);
+ else {
+ /* Emit just the prefix, then the rest. */
+ emit_line_0(ecbdata->file, set, reset, '+', "", 0);
+ ws_check_emit(line, len, ecbdata->ws_rule,
+ ecbdata->file, set, reset, ws);
+ }
+}
+
static struct diff_tempfile *claim_diff_tempfile(void) {
int i;
for (i = 0; i < ARRAY_SIZE(diff_temp); i++)
@@ -201,29 +370,6 @@ static void remove_tempfile_on_signal(int signo)
raise(signo);
}
-static int count_lines(const char *data, int size)
-{
- int count, ch, completely_empty = 1, nl_just_seen = 0;
- count = 0;
- while (0 < size--) {
- ch = *data++;
- if (ch == '\n') {
- count++;
- nl_just_seen = 1;
- completely_empty = 0;
- }
- else {
- nl_just_seen = 0;
- completely_empty = 0;
- }
- }
- if (completely_empty)
- return 0;
- if (!nl_just_seen)
- count++; /* no trailing newline */
- return count;
-}
-
static void print_line_count(FILE *file, int count)
{
switch (count) {
@@ -239,26 +385,36 @@ static void print_line_count(FILE *file, int count)
}
}
-static void copy_file_with_prefix(FILE *file,
- int prefix, const char *data, int size,
- const char *set, const char *reset)
+static void emit_rewrite_lines(struct emit_callback *ecb,
+ int prefix, const char *data, int size)
{
- int ch, nl_just_seen = 1;
- while (0 < size--) {
- ch = *data++;
- if (nl_just_seen) {
- fputs(set, file);
- putc(prefix, file);
+ const char *endp = NULL;
+ static const char *nneof = " No newline at end of file\n";
+ const char *old = diff_get_color(ecb->color_diff, DIFF_FILE_OLD);
+ const char *reset = diff_get_color(ecb->color_diff, DIFF_RESET);
+
+ while (0 < size) {
+ int len;
+
+ endp = memchr(data, '\n', size);
+ len = endp ? (endp - data + 1) : size;
+ if (prefix != '+') {
+ ecb->lno_in_preimage++;
+ emit_line_0(ecb->file, old, reset, '-',
+ data, len);
+ } else {
+ ecb->lno_in_postimage++;
+ emit_add_line(reset, ecb, data, len);
}
- if (ch == '\n') {
- nl_just_seen = 1;
- fputs(reset, file);
- } else
- nl_just_seen = 0;
- putc(ch, file);
+ size -= len;
+ data += len;
+ }
+ if (!endp) {
+ const char *plain = diff_get_color(ecb->color_diff,
+ DIFF_PLAIN);
+ emit_line_0(ecb->file, plain, reset, '\\',
+ nneof, strlen(nneof));
}
- if (!nl_just_seen)
- fprintf(file, "%s\n\\ No newline at end of file\n", reset);
}
static void emit_rewrite_diff(const char *name_a,
@@ -274,13 +430,12 @@ static void emit_rewrite_diff(const char *name_a,
const char *name_a_tab, *name_b_tab;
const char *metainfo = diff_get_color(color_diff, DIFF_METAINFO);
const char *fraginfo = diff_get_color(color_diff, DIFF_FRAGINFO);
- const char *old = diff_get_color(color_diff, DIFF_FILE_OLD);
- const char *new = diff_get_color(color_diff, DIFF_FILE_NEW);
const char *reset = diff_get_color(color_diff, DIFF_RESET);
static struct strbuf a_name = STRBUF_INIT, b_name = STRBUF_INIT;
const char *a_prefix, *b_prefix;
const char *data_one, *data_two;
size_t size_one, size_two;
+ struct emit_callback ecbdata;
if (diff_mnemonic_prefix && DIFF_OPT_TST(o, REVERSE_DIFF)) {
a_prefix = o->b_prefix;
@@ -321,6 +476,22 @@ static void emit_rewrite_diff(const char *name_a,
size_two = two->size;
}
+ memset(&ecbdata, 0, sizeof(ecbdata));
+ ecbdata.color_diff = color_diff;
+ ecbdata.found_changesp = &o->found_changes;
+ ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
+ ecbdata.file = o->file;
+ if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
+ mmfile_t mf1, mf2;
+ mf1.ptr = (char *)data_one;
+ mf2.ptr = (char *)data_two;
+ mf1.size = size_one;
+ mf2.size = size_two;
+ check_blank_at_eof(&mf1, &mf2, &ecbdata);
+ }
+ ecbdata.lno_in_preimage = 1;
+ ecbdata.lno_in_postimage = 1;
+
lc_a = count_lines(data_one, size_one);
lc_b = count_lines(data_two, size_two);
fprintf(o->file,
@@ -332,24 +503,9 @@ static void emit_rewrite_diff(const char *name_a,
print_line_count(o->file, lc_b);
fprintf(o->file, " @@%s\n", reset);
if (lc_a)
- copy_file_with_prefix(o->file, '-', data_one, size_one, old, reset);
+ emit_rewrite_lines(&ecbdata, '-', data_one, size_one);
if (lc_b)
- copy_file_with_prefix(o->file, '+', data_two, size_two, new, reset);
-}
-
-static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
-{
- if (!DIFF_FILE_VALID(one)) {
- mf->ptr = (char *)""; /* does not matter */
- mf->size = 0;
- return 0;
- }
- else if (diff_populate_filespec(one, 0))
- return -1;
-
- mf->ptr = one->data;
- mf->size = one->size;
- return 0;
+ emit_rewrite_lines(&ecbdata, '+', data_two, size_two);
}
struct diff_words_buffer {
@@ -529,18 +685,6 @@ static void diff_words_show(struct diff_words_data *diff_words)
diff_words->minus.text.size = diff_words->plus.text.size = 0;
}
-typedef unsigned long (*sane_truncate_fn)(char *line, unsigned long len);
-
-struct emit_callback {
- int nparents, color_diff;
- unsigned ws_rule;
- sane_truncate_fn truncate;
- const char **label_path;
- struct diff_words_data *diff_words;
- int *found_changesp;
- FILE *file;
-};
-
static void free_diff_words_data(struct emit_callback *ecbdata)
{
if (ecbdata->diff_words) {
@@ -566,42 +710,6 @@ const char *diff_get_color(int diff_use_color, enum color_diff ix)
return "";
}
-static void emit_line(FILE *file, const char *set, const char *reset, const char *line, int len)
-{
- int has_trailing_newline, has_trailing_carriage_return;
-
- has_trailing_newline = (len > 0 && line[len-1] == '\n');
- if (has_trailing_newline)
- len--;
- has_trailing_carriage_return = (len > 0 && line[len-1] == '\r');
- if (has_trailing_carriage_return)
- len--;
-
- fputs(set, file);
- fwrite(line, len, 1, file);
- fputs(reset, file);
- if (has_trailing_carriage_return)
- fputc('\r', file);
- if (has_trailing_newline)
- fputc('\n', file);
-}
-
-static void emit_add_line(const char *reset, struct emit_callback *ecbdata, const char *line, int len)
-{
- const char *ws = diff_get_color(ecbdata->color_diff, DIFF_WHITESPACE);
- const char *set = diff_get_color(ecbdata->color_diff, DIFF_FILE_NEW);
-
- if (!*ws)
- emit_line(ecbdata->file, set, reset, line, len);
- else {
- /* Emit just the prefix, then the rest. */
- emit_line(ecbdata->file, set, reset, line, ecbdata->nparents);
- ws_check_emit(line + ecbdata->nparents,
- len - ecbdata->nparents, ecbdata->ws_rule,
- ecbdata->file, set, reset, ws);
- }
-}
-
static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, unsigned long len)
{
const char *cp;
@@ -620,10 +728,23 @@ static unsigned long sane_truncate_line(struct emit_callback *ecb, char *line, u
return allot - l;
}
+static void find_lno(const char *line, struct emit_callback *ecbdata)
+{
+ const char *p;
+ ecbdata->lno_in_preimage = 0;
+ ecbdata->lno_in_postimage = 0;
+ p = strchr(line, '-');
+ if (!p)
+ return; /* cannot happen */
+ ecbdata->lno_in_preimage = strtol(p + 1, NULL, 10);
+ p = strchr(p, '+');
+ if (!p)
+ return; /* cannot happen */
+ ecbdata->lno_in_postimage = strtol(p + 1, NULL, 10);
+}
+
static void fn_out_consume(void *priv, char *line, unsigned long len)
{
- int i;
- int color;
struct emit_callback *ecbdata = priv;
const char *meta = diff_get_color(ecbdata->color_diff, DIFF_METAINFO);
const char *plain = diff_get_color(ecbdata->color_diff, DIFF_PLAIN);
@@ -650,14 +771,9 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
len = 1;
}
- /* This is not really necessary for now because
- * this codepath only deals with two-way diffs.
- */
- for (i = 0; i < len && line[i] == '@'; i++)
- ;
- if (2 <= i && i < len && line[i] == ' ') {
- ecbdata->nparents = i - 1;
+ if (line[0] == '@') {
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);
@@ -666,15 +782,11 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
return;
}
- if (len < ecbdata->nparents) {
+ if (len < 1) {
emit_line(ecbdata->file, reset, reset, line, len);
return;
}
- color = DIFF_PLAIN;
- if (ecbdata->diff_words && ecbdata->nparents != 1)
- /* fall back to normal diff */
- free_diff_words_data(ecbdata);
if (ecbdata->diff_words) {
if (line[0] == '-') {
diff_words_append(line, len,
@@ -693,20 +805,19 @@ static void fn_out_consume(void *priv, char *line, unsigned long len)
emit_line(ecbdata->file, plain, reset, line, len);
return;
}
- for (i = 0; i < ecbdata->nparents && len; i++) {
- if (line[i] == '-')
- color = DIFF_FILE_OLD;
- else if (line[i] == '+')
- color = DIFF_FILE_NEW;
- }
- if (color != DIFF_FILE_NEW) {
- emit_line(ecbdata->file,
- diff_get_color(ecbdata->color_diff, color),
- reset, line, len);
- return;
+ if (line[0] != '+') {
+ const char *color =
+ diff_get_color(ecbdata->color_diff,
+ line[0] == '-' ? DIFF_FILE_OLD : DIFF_PLAIN);
+ ecbdata->lno_in_preimage++;
+ if (line[0] == ' ')
+ ecbdata->lno_in_postimage++;
+ emit_line(ecbdata->file, color, reset, line, len);
+ } else {
+ ecbdata->lno_in_postimage++;
+ emit_add_line(reset, ecbdata, line + 1, len - 1);
}
- emit_add_line(reset, ecbdata, line, len);
}
static char *pprint_rename(const char *a, const char *b)
@@ -999,7 +1110,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
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;
@@ -1211,7 +1322,6 @@ struct checkdiff_t {
struct diff_options *o;
unsigned ws_rule;
unsigned status;
- int trailing_blanks_start;
};
static int is_conflict_marker(const char *line, unsigned long len)
@@ -1255,10 +1365,6 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
if (line[0] == '+') {
unsigned bad;
data->lineno++;
- if (!ws_blank_line(line + 1, len - 1, data->ws_rule))
- data->trailing_blanks_start = 0;
- else if (!data->trailing_blanks_start)
- data->trailing_blanks_start = data->lineno;
if (is_conflict_marker(line + 1, len - 1)) {
data->status |= 1;
fprintf(data->o->file,
@@ -1278,14 +1384,12 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)
data->o->file, set, reset, ws);
} else if (line[0] == ' ') {
data->lineno++;
- data->trailing_blanks_start = 0;
} else if (line[0] == '@') {
char *plus = strchr(line, '+');
if (plus)
data->lineno = strtol(plus, NULL, 10) - 1;
else
die("invalid diff");
- data->trailing_blanks_start = 0;
}
}
@@ -1562,6 +1666,8 @@ static void builtin_diff(const char *name_a,
ecbdata.color_diff = DIFF_OPT_TST(o, COLOR_DIFF);
ecbdata.found_changesp = &o->found_changes;
ecbdata.ws_rule = whitespace_rule(name_b ? name_b : name_a);
+ if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
+ check_blank_at_eof(&mf1, &mf2, &ecbdata);
ecbdata.file = o->file;
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = o->context;
@@ -1704,11 +1810,22 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
xdi_diff_outf(&mf1, &mf2, checkdiff_consume, &data,
&xpp, &xecfg, &ecb);
- if ((data.ws_rule & WS_TRAILING_SPACE) &&
- data.trailing_blanks_start) {
- fprintf(o->file, "%s:%d: ends with blank lines.\n",
- data.filename, data.trailing_blanks_start);
- data.status = 1; /* report errors */
+ if (data.ws_rule & WS_BLANK_AT_EOF) {
+ struct emit_callback ecbdata;
+ int blank_at_eof;
+
+ ecbdata.ws_rule = data.ws_rule;
+ check_blank_at_eof(&mf1, &mf2, &ecbdata);
+ blank_at_eof = ecbdata.blank_at_eof_in_preimage;
+
+ if (blank_at_eof) {
+ static char *err;
+ if (!err)
+ err = whitespace_error_string(WS_BLANK_AT_EOF);
+ fprintf(o->file, "%s:%d: %s.\n",
+ data.filename, blank_at_eof, err);
+ data.status = 1; /* report errors */
+ }
}
}
free_and_return:
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 392efb913f..69aeaf03ec 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -259,7 +259,7 @@ sub list_modified {
@tracked = map {
chomp $_;
unquote_path($_);
- } run_cmd_pipe(qw(git ls-files --exclude-standard --), @ARGV);
+ } run_cmd_pipe(qw(git ls-files --), @ARGV);
return if (!@tracked);
}
diff --git a/git-instaweb.sh b/git-instaweb.sh
index d96eddbe56..622a5f0eb2 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -317,7 +317,21 @@ EOF
resolve_full_httpd
list_mods=$(echo "$full_httpd" | sed "s/-f$/-l/")
$list_mods | grep 'mod_cgi\.c' >/dev/null 2>&1 || \
- echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
+ if test -f "$module_path/mod_cgi.so"
+ then
+ echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
+ else
+ $list_mods | grep 'mod_cgid\.c' >/dev/null 2>&1 || \
+ if test -f "$module_path/mod_cgid.so"
+ then
+ echo "LoadModule cgid_module $module_path/mod_cgid.so" \
+ >> "$conf"
+ else
+ echo "You have no CGI support!"
+ exit 2
+ fi
+ echo "ScriptSock logs/gitweb.sock" >> "$conf"
+ fi
cat >> "$conf" <<EOF
AddHandler cgi-script .cgi
<Location /gitweb.cgi>
diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh
index 23ded48322..a1879e3ace 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -340,6 +340,14 @@ do_next () {
pick_one $sha1 ||
die_with_patch $sha1 "Could not apply $sha1... $rest"
;;
+ reword|r)
+ comment_for_reflog reword
+
+ mark_action_done
+ pick_one $sha1 ||
+ die_with_patch $sha1 "Could not apply $sha1... $rest"
+ git commit --amend
+ ;;
edit|e)
comment_for_reflog edit
@@ -752,6 +760,7 @@ first and then run 'git rebase --continue' again."
#
# Commands:
# p, pick = use commit
+# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
#
diff --git a/git-send-email.perl b/git-send-email.perl
index f5ba4e7699..a0279de687 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -835,7 +835,7 @@ sub send_message
$gitversion = Git::version();
}
- my $cc = join(", ", unique_email_list(@cc));
+ my $cc = join(",\n\t", unique_email_list(@cc));
my $ccline = "";
if ($cc ne '') {
$ccline = "\nCc: $cc";
@@ -976,7 +976,9 @@ X-Mailer: git-send-email $gitversion
if ($smtp_server !~ m#^/#) {
print "Server: $smtp_server\n";
print "MAIL FROM:<$raw_from>\n";
- print "RCPT TO:".join(',',(map { "<$_>" } @recipients))."\n";
+ foreach my $entry (@recipients) {
+ print "RCPT TO:<$entry>\n";
+ }
} else {
print "Sendmail: $smtp_server ".join(' ',@sendmail_parameters)."\n";
}
diff --git a/git.c b/git.c
index 9883009b5d..bd2c5fe77b 100644
--- a/git.c
+++ b/git.c
@@ -6,7 +6,7 @@
const char git_usage_string[] =
"git [--version] [--exec-path[=GIT_EXEC_PATH]] [--html-path]\n"
- " [-p|--paginate|--no-pager]\n"
+ " [-p|--paginate|--no-pager] [--no-replace-objects]\n"
" [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE]\n"
" [--help] COMMAND [ARGS]";
@@ -87,6 +87,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
use_pager = 0;
if (envchanged)
*envchanged = 1;
+ } else if (!strcmp(cmd, "--no-replace-objects")) {
+ read_replace_refs = 0;
} else if (!strcmp(cmd, "--git-dir")) {
if (*argc < 2) {
fprintf(stderr, "No directory given for --git-dir.\n" );
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index 8f68fe3091..d60bfc1f64 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -341,6 +341,12 @@ td.mode {
font-family: monospace;
}
+/* format of (optional) objects size in 'tree' view */
+td.size {
+ font-family: monospace;
+ text-align: right;
+}
+
/* styling of diffs (patchsets): commitdiff and blobdiff views */
div.diff.header,
div.diff.extended_header {
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 24b219310a..c9e2af67ca 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -297,6 +297,19 @@ our %feature = (
'override' => 0,
'default' => [1]},
+ # Enable showing size of blobs in a 'tree' view, in a separate
+ # column, similar to what 'ls -l' does. This cost a bit of IO.
+
+ # To disable system wide have in $GITWEB_CONFIG
+ # $feature{'show-sizes'}{'default'} = [0];
+ # To have project specific config enable override in $GITWEB_CONFIG
+ # $feature{'show-sizes'}{'override'} = 1;
+ # and in project config gitweb.showsizes = 0|1;
+ 'show-sizes' => {
+ 'sub' => sub { feature_bool('showsizes', @_) },
+ 'override' => 0,
+ 'default' => [1]},
+
# Make gitweb use an alternative format of the URLs which can be
# more readable and natural-looking: project name is embedded
# directly in the path and the query string contains other
@@ -1083,8 +1096,7 @@ sub to_utf8 {
# correct, but quoted slashes look too horrible in bookmarks
sub esc_param {
my $str = shift;
- $str =~ s/([^A-Za-z0-9\-_.~()\/:@])/sprintf("%%%02X", ord($1))/eg;
- $str =~ s/\+/%2B/g;
+ $str =~ s/([^A-Za-z0-9\-_.~()\/:@ ]+)/CGI::escape($1)/eg;
$str =~ s/ /\+/g;
return $str;
}
@@ -2764,16 +2776,31 @@ sub parse_ls_tree_line {
my %opts = @_;
my %res;
- #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
- $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
+ if ($opts{'-l'}) {
+ #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa 16717 panic.c'
+ $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40}) +(-|[0-9]+)\t(.+)$/s;
- $res{'mode'} = $1;
- $res{'type'} = $2;
- $res{'hash'} = $3;
- if ($opts{'-z'}) {
- $res{'name'} = $4;
+ $res{'mode'} = $1;
+ $res{'type'} = $2;
+ $res{'hash'} = $3;
+ $res{'size'} = $4;
+ if ($opts{'-z'}) {
+ $res{'name'} = $5;
+ } else {
+ $res{'name'} = unquote($5);
+ }
} else {
- $res{'name'} = unquote($4);
+ #'100644 blob 0fa3f3a66fb6a137f6ec2c19351ed4d807070ffa panic.c'
+ $line =~ m/^([0-9]+) (.+) ([0-9a-fA-F]{40})\t(.+)$/s;
+
+ $res{'mode'} = $1;
+ $res{'type'} = $2;
+ $res{'hash'} = $3;
+ if ($opts{'-z'}) {
+ $res{'name'} = $4;
+ } else {
+ $res{'name'} = unquote($4);
+ }
}
return wantarray ? %res : \%res;
@@ -3564,6 +3591,9 @@ sub git_print_tree_entry {
# and link is the action links of the entry.
print "<td class=\"mode\">" . mode_str($t->{'mode'}) . "</td>\n";
+ if (exists $t->{'size'}) {
+ print "<td class=\"size\">$t->{'size'}</td>\n";
+ }
if ($t->{'type'} eq "blob") {
print "<td class=\"list\">" .
$cgi->a({-href => href(action=>"blob", hash=>$t->{'hash'},
@@ -3609,12 +3639,14 @@ sub git_print_tree_entry {
} elsif ($t->{'type'} eq "tree") {
print "<td class=\"list\">";
print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key)},
+ file_name=>"$basedir$t->{'name'}",
+ %base_key)},
esc_path($t->{'name'}));
print "</td>\n";
print "<td class=\"link\">";
print $cgi->a({-href => href(action=>"tree", hash=>$t->{'hash'},
- file_name=>"$basedir$t->{'name'}", %base_key)},
+ file_name=>"$basedir$t->{'name'}",
+ %base_key)},
"tree");
if (defined $hash_base) {
print " | " .
@@ -5088,10 +5120,14 @@ sub git_tree {
}
die_error(404, "No such tree") unless defined($hash);
+ my $show_sizes = gitweb_check_feature('show-sizes');
+ my $have_blame = gitweb_check_feature('blame');
+
my @entries = ();
{
local $/ = "\0";
- open my $fd, "-|", git_cmd(), "ls-tree", '-z', $hash
+ open my $fd, "-|", git_cmd(), "ls-tree", '-z',
+ ($show_sizes ? '-l' : ()), @extra_options, $hash
or die_error(500, "Open git-ls-tree failed");
@entries = map { chomp; $_ } <$fd>;
close $fd
@@ -5102,7 +5138,6 @@ sub git_tree {
my $ref = format_ref_marker($refs, $hash_base);
git_header_html();
my $basedir = '';
- my $have_blame = gitweb_check_feature('blame');
if (defined $hash_base && (my %co = parse_commit($hash_base))) {
my @views_nav = ();
if (defined $file_name) {
@@ -5118,7 +5153,8 @@ sub git_tree {
# FIXME: Should be available when we have no hash base as well.
push @views_nav, $snapshot_links;
}
- git_print_page_nav('tree','', $hash_base, undef, undef, join(' | ', @views_nav));
+ git_print_page_nav('tree','', $hash_base, undef, undef,
+ join(' | ', @views_nav));
git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash_base);
} else {
undef $hash_base;
@@ -5151,8 +5187,10 @@ sub git_tree {
undef $up unless $up;
# based on git_print_tree_entry
print '<td class="mode">' . mode_str('040000') . "</td>\n";
+ print '<td class="size">&nbsp;</td>'."\n" if $show_sizes;
print '<td class="list">';
- print $cgi->a({-href => href(action=>"tree", hash_base=>$hash_base,
+ print $cgi->a({-href => href(action=>"tree",
+ hash_base=>$hash_base,
file_name=>$up)},
"..");
print "</td>\n";
@@ -5161,7 +5199,7 @@ sub git_tree {
print "</tr>\n";
}
foreach my $line (@entries) {
- my %t = parse_ls_tree_line($line, -z => 1);
+ my %t = parse_ls_tree_line($line, -z => 1, -l => $show_sizes);
if ($alternate) {
print "<tr class=\"dark\">\n";
@@ -5328,7 +5366,7 @@ sub git_commit {
} @$parents ) .
')';
}
- if (gitweb_check_feature('patches')) {
+ if (gitweb_check_feature('patches') && @$parents <= 1) {
$formats_nav .= " | " .
$cgi->a({-href => href(action=>"patch", -replay=>1)},
"patch");
@@ -5616,7 +5654,7 @@ sub git_commitdiff {
$formats_nav =
$cgi->a({-href => href(action=>"commitdiff_plain", -replay=>1)},
"raw");
- if ($patch_max) {
+ if ($patch_max && @{$co{'parents'}} <= 1) {
$formats_nav .= " | " .
$cgi->a({-href => href(action=>"patch", -replay=>1)},
"patch");
@@ -5824,7 +5862,7 @@ sub git_commitdiff_plain {
# format-patch-style patches
sub git_patch {
- git_commitdiff(-format => 'patch', -single=> 1);
+ git_commitdiff(-format => 'patch', -single => 1);
}
sub git_patches {
diff --git a/log-tree.c b/log-tree.c
index 1618f3c79a..f7d54f2f1b 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -43,6 +43,7 @@ void load_ref_decorations(int flags)
if (!loaded) {
loaded = 1;
for_each_ref(add_ref_decoration, &flags);
+ head_ref(add_ref_decoration, &flags);
}
}
diff --git a/pretty.c b/pretty.c
index f5983f8baa..587101f846 100644
--- a/pretty.c
+++ b/pretty.c
@@ -740,7 +740,7 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
}
void format_commit_message(const struct commit *commit,
- const void *format, struct strbuf *sb,
+ const char *format, struct strbuf *sb,
enum date_mode dmode)
{
struct format_commit_context context;
diff --git a/t/lib-rebase.sh b/t/lib-rebase.sh
index 260a231933..62f452c8ea 100644
--- a/t/lib-rebase.sh
+++ b/t/lib-rebase.sh
@@ -9,8 +9,8 @@
#
# "[<lineno1>] [<lineno2>]..."
#
-# If a line number is prefixed with "squash" or "edit", the respective line's
-# command will be replaced with the specified one.
+# If a line number is prefixed with "squash", "edit", or "reword", the
+# respective line's command will be replaced with the specified one.
set_fake_editor () {
echo "#!$SHELL_PATH" >fake-editor.sh
@@ -32,7 +32,7 @@ cat "$1".tmp
action=pick
for line in $FAKE_LINES; do
case $line in
- squash|edit)
+ squash|edit|reword)
action="$line";;
*)
echo sed -n "${line}s/^pick/$action/p"
diff --git a/t/t3003-ls-files-exclude.sh b/t/t3003-ls-files-exclude.sh
new file mode 100755
index 0000000000..fc1e379321
--- /dev/null
+++ b/t/t3003-ls-files-exclude.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+test_description='ls-files --exclude does not affect index files'
+. ./test-lib.sh
+
+test_expect_success 'create repo with file' '
+ echo content >file &&
+ git add file &&
+ git commit -m file &&
+ echo modification >file
+'
+
+check_output() {
+test_expect_success "ls-files output contains file ($1)" "
+ echo '$2' >expect &&
+ git ls-files --exclude-standard --$1 >output &&
+ test_cmp expect output
+"
+}
+
+check_all_output() {
+ check_output 'cached' 'file'
+ check_output 'modified' 'file'
+}
+
+check_all_output
+test_expect_success 'add file to gitignore' '
+ echo file >.gitignore
+'
+check_all_output
+
+test_done
diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index 4cae019521..3a37793c0d 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -470,4 +470,18 @@ test_expect_success 'avoid unnecessary reset' '
test 123456789 = $MTIME
'
+test_expect_success 'reword' '
+ git checkout -b reword-branch master &&
+ FAKE_LINES="1 2 3 reword 4" FAKE_COMMIT_MESSAGE="E changed" git rebase -i A &&
+ git show HEAD | grep "E changed" &&
+ test $(git rev-parse master) != $(git rev-parse HEAD) &&
+ test $(git rev-parse master^) = $(git rev-parse HEAD^) &&
+ FAKE_LINES="1 2 reword 3 4" FAKE_COMMIT_MESSAGE="D changed" git rebase -i A &&
+ git show HEAD^ | grep "D changed" &&
+ FAKE_LINES="reword 1 2 3 4" FAKE_COMMIT_MESSAGE="B changed" git rebase -i A &&
+ git show HEAD~3 | grep "B changed" &&
+ FAKE_LINES="1 reword 2 3 4" FAKE_COMMIT_MESSAGE="C changed" git rebase -i A &&
+ git show HEAD~2 | grep "C changed"
+'
+
test_done
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index 62fd65e18d..687bd7ab53 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -138,6 +138,20 @@ test_expect_success 'real edit works' '
test_cmp expected output
'
+test_expect_success 'skip files similarly as commit -a' '
+ git reset &&
+ echo file >.gitignore &&
+ echo changed >file &&
+ echo y | git add -p file &&
+ git diff >output &&
+ git reset &&
+ git commit -am commit &&
+ git diff >expected &&
+ test_cmp expected output &&
+ git reset --hard HEAD^
+'
+rm -f .gitignore
+
if test "$(git config --bool core.filemode)" = false
then
say 'skipping filemode tests (filesystem does not properly support modes)'
diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all
index 903d9d9659..d155e0bab2 100644
--- a/t/t4013/diff.log_--decorate=full_--all
+++ b/t/t4013/diff.log_--decorate=full_--all
@@ -1,5 +1,5 @@
$ git log --decorate=full --all
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (refs/heads/master)
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, refs/heads/master)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
diff --git a/t/t4013/diff.log_--decorate_--all b/t/t4013/diff.log_--decorate_--all
index 954210ea90..fd7c3e6439 100644
--- a/t/t4013/diff.log_--decorate_--all
+++ b/t/t4013/diff.log_--decorate_--all
@@ -1,5 +1,5 @@
$ git log --decorate --all
-commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (master)
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (HEAD, master)
Merge: 9a6d494 c7a2ab9
Author: A U Thor <author@example.com>
Date: Mon Jun 26 00:04:00 2006 +0000
diff --git a/t/t4015-diff-whitespace.sh b/t/t4015-diff-whitespace.sh
index 6d13da30da..8dd147d78f 100755
--- a/t/t4015-diff-whitespace.sh
+++ b/t/t4015-diff-whitespace.sh
@@ -362,10 +362,17 @@ test_expect_success 'line numbers in --check output are correct' '
'
-test_expect_success 'checkdiff detects trailing blank lines' '
+test_expect_success 'checkdiff detects new trailing blank lines (1)' '
echo "foo();" >x &&
echo "" >>x &&
- git diff --check | grep "ends with blank"
+ git diff --check | grep "new blank line"
+'
+
+test_expect_success 'checkdiff detects new trailing blank lines (2)' '
+ { echo a; echo b; echo; echo; } >x &&
+ git add x &&
+ { echo a; echo; echo; echo; echo; } >x &&
+ git diff --check | grep "new blank line"
'
test_expect_success 'checkdiff allows new blank lines' '
diff --git a/t/t4019-diff-wserror.sh b/t/t4019-diff-wserror.sh
index 84a1fe3115..3a3663fbcb 100755
--- a/t/t4019-diff-wserror.sh
+++ b/t/t4019-diff-wserror.sh
@@ -165,7 +165,7 @@ test_expect_success 'trailing empty lines (1)' '
rm -f .gitattributes &&
test_must_fail git diff --check >output &&
- grep "ends with blank lines." output &&
+ grep "new blank line at" output &&
grep "trailing whitespace" output
'
@@ -190,4 +190,13 @@ test_expect_success 'do not color trailing cr in context' '
'
+test_expect_success 'color new trailing blank lines' '
+ { echo a; echo b; echo; echo; } >x &&
+ git add x &&
+ { echo a; echo; echo; echo; echo c; echo; echo; echo; echo; } >x &&
+ git diff --color x >output &&
+ cnt=$(grep "${blue_grep}" output | wc -l) &&
+ test $cnt = 2
+'
+
test_done
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index fac2093d7f..ca26397590 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -170,4 +170,95 @@ test_expect_success 'trailing whitespace & no newline at the end of file' '
grep "^$" target
'
+test_expect_success 'blank at EOF with --whitespace=fix (1)' '
+ : these can fail depending on what we did before
+ git config --unset core.whitespace
+ rm -f .gitattributes
+
+ { echo a; echo b; echo c; } >one &&
+ git add one &&
+ { echo a; echo b; echo c; } >expect &&
+ { cat expect; echo; } >one &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ git apply --whitespace=fix patch &&
+ test_cmp expect one
+'
+
+test_expect_success 'blank at EOF with --whitespace=fix (2)' '
+ { echo a; echo b; echo c; } >one &&
+ git add one &&
+ { echo a; echo c; } >expect &&
+ { cat expect; echo; echo; } >one &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ git apply --whitespace=fix patch &&
+ test_cmp expect one
+'
+
+test_expect_success 'blank at EOF with --whitespace=fix (3)' '
+ { echo a; echo b; echo; } >one &&
+ git add one &&
+ { echo a; echo c; echo; } >expect &&
+ { cat expect; echo; echo; } >one &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ git apply --whitespace=fix patch &&
+ test_cmp expect one
+'
+
+test_expect_success 'blank at end of hunk, not at EOF with --whitespace=fix' '
+ { echo a; echo b; echo; echo; echo; echo; echo; echo d; } >one &&
+ git add one &&
+ { echo a; echo c; echo; echo; echo; echo; echo; echo; echo d; } >expect &&
+ cp expect one &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ git apply --whitespace=fix patch &&
+ test_cmp expect one
+'
+
+test_expect_success 'blank at EOF with --whitespace=warn' '
+ { echo a; echo b; echo c; } >one &&
+ git add one &&
+ echo >>one &&
+ cat one >expect &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ git apply --whitespace=warn patch 2>error &&
+ test_cmp expect one &&
+ grep "new blank line at EOF" error
+'
+
+test_expect_success 'blank at EOF with --whitespace=error' '
+ { echo a; echo b; echo c; } >one &&
+ git add one &&
+ cat one >expect &&
+ echo >>one &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ test_must_fail git apply --whitespace=error patch 2>error &&
+ test_cmp expect one &&
+ grep "new blank line at EOF" error
+'
+
+test_expect_success 'blank but not empty at EOF' '
+ { echo a; echo b; echo c; } >one &&
+ git add one &&
+ echo " " >>one &&
+ cat one >expect &&
+ git diff -- one >patch &&
+
+ git checkout one &&
+ git apply --whitespace=warn patch 2>error &&
+ test_cmp expect one &&
+ grep "new blank line at EOF" error
+'
+
test_done
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
index 8b8bd81c09..d4818b430a 100755
--- a/t/t6050-replace.sh
+++ b/t/t6050-replace.sh
@@ -70,6 +70,13 @@ test_expect_success 'replace the author' '
git show $HASH2 | grep "O Thor"
'
+test_expect_success 'test --no-replace-objects option' '
+ git cat-file commit $HASH2 | grep "author O Thor" &&
+ git --no-replace-objects cat-file commit $HASH2 | grep "author A U Thor" &&
+ git show $HASH2 | grep "O Thor" &&
+ git --no-replace-objects show $HASH2 | grep "A U Thor"
+'
+
cat >tag.sig <<EOF
object $HASH2
type commit
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index fb606a9f05..84a7f03d46 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -103,10 +103,18 @@ cat >expected-show-all-headers <<\EOF
Dry-OK. Log says:
Server: relay.example.com
MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<bcc@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<cc@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+RCPT TO:<bcc@example.com>
From: Example <from@example.com>
To: to@example.com
-Cc: cc@example.com, A <author@example.com>, One <one@example.com>, two@example.com
+Cc: cc@example.com,
+ A <author@example.com>,
+ One <one@example.com>,
+ two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
Message-Id: MESSAGE-ID-STRING
@@ -164,7 +172,7 @@ test_expect_success 'cccmd works' '
--smtp-server="$(pwd)/fake.sendmail" \
cccmd.patch \
&&
- grep ^Cc:.*cccmd@example.com msgtxt1
+ grep "^ cccmd@example.com" msgtxt1
'
z8=zzzzzzzz
@@ -278,10 +286,17 @@ cat >expected-suppress-sob <<\EOF
Dry-OK. Log says:
Server: relay.example.com
MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<cc@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<cc@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
From: Example <from@example.com>
To: to@example.com
-Cc: cc@example.com, A <author@example.com>, One <one@example.com>, two@example.com
+Cc: cc@example.com,
+ A <author@example.com>,
+ One <one@example.com>,
+ two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
Message-Id: MESSAGE-ID-STRING
@@ -318,10 +333,15 @@ cat >expected-suppress-sob <<\EOF
Dry-OK. Log says:
Server: relay.example.com
MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
From: Example <from@example.com>
To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com
+Cc: A <author@example.com>,
+ One <one@example.com>,
+ two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
Message-Id: MESSAGE-ID-STRING
@@ -344,10 +364,17 @@ cat >expected-suppress-cccmd <<\EOF
Dry-OK. Log says:
Server: relay.example.com
MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<committer@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+RCPT TO:<committer@example.com>
From: Example <from@example.com>
To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com, C O Mitter <committer@example.com>
+Cc: A <author@example.com>,
+ One <one@example.com>,
+ two@example.com,
+ C O Mitter <committer@example.com>
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
Message-Id: MESSAGE-ID-STRING
@@ -392,10 +419,17 @@ cat >expected-suppress-body <<\EOF
Dry-OK. Log says:
Server: relay.example.com
MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<cc-cmd@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+RCPT TO:<cc-cmd@example.com>
From: Example <from@example.com>
To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com, cc-cmd@example.com
+Cc: A <author@example.com>,
+ One <one@example.com>,
+ two@example.com,
+ cc-cmd@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
Message-Id: MESSAGE-ID-STRING
@@ -416,10 +450,15 @@ cat >expected-suppress-body-cccmd <<\EOF
Dry-OK. Log says:
Server: relay.example.com
MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
From: Example <from@example.com>
To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com
+Cc: A <author@example.com>,
+ One <one@example.com>,
+ two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
Message-Id: MESSAGE-ID-STRING
@@ -440,10 +479,15 @@ cat >expected-suppress-sob <<\EOF
Dry-OK. Log says:
Server: relay.example.com
MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
From: Example <from@example.com>
To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com
+Cc: A <author@example.com>,
+ One <one@example.com>,
+ two@example.com
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
Message-Id: MESSAGE-ID-STRING
@@ -466,10 +510,17 @@ cat >expected-suppress-bodycc <<\EOF
Dry-OK. Log says:
Server: relay.example.com
MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<one@example.com>,<two@example.com>,<committer@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<one@example.com>
+RCPT TO:<two@example.com>
+RCPT TO:<committer@example.com>
From: Example <from@example.com>
To: to@example.com
-Cc: A <author@example.com>, One <one@example.com>, two@example.com, C O Mitter <committer@example.com>
+Cc: A <author@example.com>,
+ One <one@example.com>,
+ two@example.com,
+ C O Mitter <committer@example.com>
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
Message-Id: MESSAGE-ID-STRING
@@ -489,10 +540,13 @@ cat >expected-suppress-cc <<\EOF
Dry-OK. Log says:
Server: relay.example.com
MAIL FROM:<from@example.com>
-RCPT TO:<to@example.com>,<author@example.com>,<committer@example.com>
+RCPT TO:<to@example.com>
+RCPT TO:<author@example.com>
+RCPT TO:<committer@example.com>
From: Example <from@example.com>
To: to@example.com
-Cc: A <author@example.com>, C O Mitter <committer@example.com>
+Cc: A <author@example.com>,
+ C O Mitter <committer@example.com>
Subject: [PATCH 1/1] Second.
Date: DATE-STRING
Message-Id: MESSAGE-ID-STRING
@@ -605,7 +659,7 @@ test_expect_success 'utf8 Cc is rfc2047 encoded' '
--to=nobody@example.com \
--smtp-server="$(pwd)/fake.sendmail" \
outdir/*.patch &&
- grep "^Cc:" msgtxt1 |
+ grep "^ " msgtxt1 |
grep "=?UTF-8?q?=C3=A0=C3=A9=C3=AC=C3=B6=C3=BA?= <utf8@example.com>"
'
diff --git a/ws.c b/ws.c
index 59d0883c1f..760b5743fa 100644
--- a/ws.c
+++ b/ws.c
@@ -16,6 +16,8 @@ static struct whitespace_rule {
{ "space-before-tab", WS_SPACE_BEFORE_TAB, 0 },
{ "indent-with-non-tab", WS_INDENT_WITH_NON_TAB, 0 },
{ "cr-at-eol", WS_CR_AT_EOL, 1 },
+ { "blank-at-eol", WS_BLANK_AT_EOL, 0 },
+ { "blank-at-eof", WS_BLANK_AT_EOF, 0 },
};
unsigned parse_whitespace_rule(const char *string)
@@ -102,8 +104,17 @@ unsigned whitespace_rule(const char *pathname)
char *whitespace_error_string(unsigned ws)
{
struct strbuf err = STRBUF_INIT;
- if (ws & WS_TRAILING_SPACE)
+ if ((ws & WS_TRAILING_SPACE) == WS_TRAILING_SPACE)
strbuf_addstr(&err, "trailing whitespace");
+ else {
+ if (ws & WS_BLANK_AT_EOL)
+ strbuf_addstr(&err, "trailing whitespace");
+ if (ws & WS_BLANK_AT_EOF) {
+ if (err.len)
+ strbuf_addstr(&err, ", ");
+ strbuf_addstr(&err, "new blank line at EOF");
+ }
+ }
if (ws & WS_SPACE_BEFORE_TAB) {
if (err.len)
strbuf_addstr(&err, ", ");
@@ -141,11 +152,11 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,
}
/* Check for trailing whitespace. */
- if (ws_rule & WS_TRAILING_SPACE) {
+ if (ws_rule & WS_BLANK_AT_EOL) {
for (i = len - 1; i >= 0; i--) {
if (isspace(line[i])) {
trailing_whitespace = i;
- result |= WS_TRAILING_SPACE;
+ result |= WS_BLANK_AT_EOL;
}
else
break;
@@ -261,7 +272,7 @@ int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *erro
/*
* Strip trailing whitespace
*/
- if (ws_rule & WS_TRAILING_SPACE) {
+ if (ws_rule & WS_BLANK_AT_EOL) {
if (0 < len && src[len - 1] == '\n') {
add_nl_to_tail = 1;
len--;