aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/Makefile22
-rw-r--r--Documentation/RelNotes-1.6.2.txt28
-rwxr-xr-xDocumentation/cat-texi.perl8
-rw-r--r--Documentation/config.txt10
-rw-r--r--Documentation/diff-options.txt4
-rw-r--r--Documentation/git-filter-branch.txt14
-rw-r--r--Documentation/git-mergetool.txt11
-rw-r--r--Documentation/git-reset.txt29
-rw-r--r--Documentation/technical/api-strbuf.txt11
-rw-r--r--INSTALL3
-rw-r--r--Makefile15
l---------RelNotes2
-rw-r--r--builtin-apply.c4
-rw-r--r--builtin-cat-file.c2
-rw-r--r--builtin-clone.c14
-rw-r--r--builtin-fetch.c2
-rw-r--r--builtin-init-db.c2
-rw-r--r--builtin-log.c23
-rw-r--r--builtin-mailinfo.c2
-rw-r--r--builtin-merge-recursive.c2
-rw-r--r--builtin-pack-objects.c9
-rw-r--r--builtin-reset.c26
-rw-r--r--builtin-update-index.c2
-rw-r--r--connect.c2
-rwxr-xr-xcontrib/completion/git-completion.bash7
-rw-r--r--daemon.c2
-rw-r--r--diff-lib.c40
-rw-r--r--diff.c6
-rw-r--r--diff.h1
-rwxr-xr-xgit-add--interactive.perl67
-rwxr-xr-xgit-filter-branch.sh29
-rwxr-xr-xgit-mergetool.sh110
-rw-r--r--git.c7
-rwxr-xr-xgitweb/gitweb.perl131
-rw-r--r--grep.c2
-rw-r--r--http-push.c25
-rw-r--r--imap-send.c6
-rw-r--r--index-pack.c2
-rw-r--r--merge-recursive.c37
-rw-r--r--pack-redundant.c8
-rw-r--r--strbuf.c17
-rwxr-xr-xt/t4032-diff-inter-hunk-context.sh92
-rwxr-xr-xt/t4129-apply-samemode.sh62
-rwxr-xr-xt/t5302-pack-index.sh1
-rwxr-xr-xt/t6024-recursive-merge.sh23
-rwxr-xr-xt/t7003-filter-branch.sh8
-rwxr-xr-xt/t7500-commit.sh5
-rwxr-xr-xt/t7501-commit.sh20
-rwxr-xr-xt/t7607-merge-overwrite.sh87
-rwxr-xr-xt/t9130-git-svn-authors-file.sh94
-rw-r--r--xdiff/xdiff.h1
-rw-r--r--xdiff/xemit.c3
52 files changed, 927 insertions, 213 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile
index c34c1cae2..144ec32f1 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -32,6 +32,7 @@ DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
prefix?=$(HOME)
bindir?=$(prefix)/bin
htmldir?=$(prefix)/share/doc/git-doc
+pdfdir?=$(prefix)/share/doc/git-doc
mandir?=$(prefix)/share/man
man1dir=$(mandir)/man1
man5dir=$(mandir)/man5
@@ -50,6 +51,7 @@ infodir?=$(prefix)/share/info
MAKEINFO=makeinfo
INSTALL_INFO=install-info
DOCBOOK2X_TEXI=docbook2x-texi
+DBLATEX=dblatex
ifndef PERL_PATH
PERL_PATH = /usr/bin/perl
endif
@@ -87,6 +89,8 @@ man7: $(DOC_MAN7)
info: git.info gitman.info
+pdf: user-manual.pdf
+
install: install-man
install-man: man
@@ -107,6 +111,10 @@ install-info: info
echo "No directory found in $(DESTDIR)$(infodir)" >&2 ; \
fi
+install-pdf: pdf
+ $(INSTALL) -d -m 755 $(DESTDIR)$(pdfdir)
+ $(INSTALL) -m 644 user-manual.pdf $(DESTDIR)$(pdfdir)
+
install-html: html
sh ./install-webdoc.sh $(DESTDIR)$(htmldir)
@@ -187,17 +195,23 @@ git.info: user-manual.texi
user-manual.texi: user-manual.xml
$(RM) $@+ $@
- $(DOCBOOK2X_TEXI) user-manual.xml --to-stdout | $(PERL_PATH) fix-texi.perl >$@+
+ $(DOCBOOK2X_TEXI) user-manual.xml --encoding=UTF-8 --to-stdout | \
+ $(PERL_PATH) fix-texi.perl >$@+
+ mv $@+ $@
+
+user-manual.pdf: user-manual.xml
+ $(RM) $@+ $@
+ $(DBLATEX) -o $@+ -p /etc/asciidoc/dblatex/asciidoc-dblatex.xsl -s /etc/asciidoc/dblatex/asciidoc-dblatex.sty $<
mv $@+ $@
gitman.texi: $(MAN_XML) cat-texi.perl
$(RM) $@+ $@
- ($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --to-stdout $(xml);)) | \
- $(PERL_PATH) cat-texi.perl $@ >$@+
+ ($(foreach xml,$(MAN_XML),$(DOCBOOK2X_TEXI) --encoding=UTF-8 \
+ --to-stdout $(xml);)) | $(PERL_PATH) cat-texi.perl $@ >$@+
mv $@+ $@
gitman.info: gitman.texi
- $(MAKEINFO) --no-split $*.texi
+ $(MAKEINFO) --no-split --no-validate $*.texi
$(patsubst %.txt,%.texi,$(MAN_TXT)): %.texi : %.xml
$(RM) $@+ $@
diff --git a/Documentation/RelNotes-1.6.2.txt b/Documentation/RelNotes-1.6.2.txt
new file mode 100644
index 000000000..1a8062678
--- /dev/null
+++ b/Documentation/RelNotes-1.6.2.txt
@@ -0,0 +1,28 @@
+GIT v1.6.2 Release Notes
+========================
+
+Updates since v1.6.1
+--------------------
+
+(subsystems)
+
+(portability)
+
+(performance)
+
+(usability, bells and whistles)
+
+(internal)
+
+
+Fixes since v1.6.1
+------------------
+
+All of the fixes in v1.6.1.X maintenance series are included in this
+release, unless otherwise noted.
+
+--
+exec >/var/tmp/1
+O=v1.6.1
+echo O=$(git describe master)
+git shortlog --no-merges $O..master ^maint
diff --git a/Documentation/cat-texi.perl b/Documentation/cat-texi.perl
index dbc133cd3..828ec6255 100755
--- a/Documentation/cat-texi.perl
+++ b/Documentation/cat-texi.perl
@@ -18,8 +18,12 @@ close TMP;
printf '\input texinfo
@setfilename gitman.info
-@documentencoding us-ascii
-@node Top,,%s
+@documentencoding UTF-8
+@dircategory Development
+@direntry
+* Git Man Pages: (gitman). Manual pages for Git revision control system
+@end direntry
+@node Top,,, (dir)
@top Git Manual Pages
@documentlanguage en
@menu
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 52786c7df..7408bb2d3 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1044,6 +1044,16 @@ mergetool.keepBackup::
is set to `false` then this file is not preserved. Defaults to
`true` (i.e. keep the backup files).
+mergetool.keepTemporaries::
+ When invoking a custom merge tool, git uses a set of temporary
+ files to pass to the tool. If the tool returns an error and this
+ variable is set to `true`, then these temporary files will be
+ preserved, otherwise they will be removed after the tool has
+ exited. Defaults to `false`.
+
+mergetool.prompt::
+ Prompt before each invocation of the merge resolution program.
+
pack.window::
The size of the window used by linkgit:git-pack-objects[1] when no
window size is given on the command line. Defaults to 10.
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index b432d2518..671f533ca 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -205,6 +205,10 @@ endif::git-format-patch[]
differences even if one line has whitespace where the other
line has none.
+--inter-hunk-context=<lines>::
+ Show the context between diff hunks, up to the specified number
+ of lines, thereby fusing hunks that are close to each other.
+
--exit-code::
Make the program exit with codes similar to diff(1).
That is, it exits with 1 if there were differences and
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index fed6de6a7..451950bab 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -122,6 +122,10 @@ You can use the 'map' convenience function in this filter, and other
convenience functions, too. For example, calling 'skip_commit "$@"'
will leave out the current commit (but not its changes! If you want
that, use 'git-rebase' instead).
++
+You can also use the 'git_commit_non_empty_tree "$@"' instead of
+'git commit-tree "$@"' if you don't wish to keep commits with a single parent
+and that makes no change to the tree.
--tag-name-filter <command>::
This is the filter for rewriting tag names. When passed,
@@ -151,6 +155,16 @@ to other tags will be rewritten to point to the underlying commit.
The result will contain that directory (and only that) as its
project root.
+--prune-empty::
+ Some kind of filters will generate empty commits, that left the tree
+ untouched. This switch allow git-filter-branch to ignore such
+ commits. Though, this switch only applies for commits that have one
+ and only one parent, it will hence keep merges points. Also, this
+ option is not compatible with the use of '--commit-filter'. Though you
+ just need to use the function 'git_commit_non_empty_tree "$@"' instead
+ of the 'git commit-tree "$@"' idiom in your commit filter to make that
+ happen.
+
--original <namespace>::
Use this option to set the namespace where the original commits
will be stored. The default value is 'refs/original'.
diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt
index 602e7c6d3..4c0ffec50 100644
--- a/Documentation/git-mergetool.txt
+++ b/Documentation/git-mergetool.txt
@@ -7,7 +7,7 @@ git-mergetool - Run merge conflict resolution tools to resolve merge conflicts
SYNOPSIS
--------
-'git mergetool' [--tool=<tool>] [<file>]...
+'git mergetool' [--tool=<tool>] [-y|--no-prompt|--prompt] [<file>]...
DESCRIPTION
-----------
@@ -60,6 +60,15 @@ variable `mergetool.<tool>.trustExitCode` can be set to `true`.
Otherwise, 'git-mergetool' will prompt the user to indicate the
success of the resolution after the custom tool has exited.
+-y or --no-prompt::
+ Don't prompt before each invocation of the merge resolution
+ program.
+
+--prompt::
+ Prompt before each invocation of the merge resolution program.
+ This is the default behaviour; the option is provided to
+ override any configuration settings.
+
Author
------
Written by Theodore Y Ts'o <tytso@mit.edu>
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 2049f3d97..abb25d1c0 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -8,7 +8,7 @@ git-reset - Reset current HEAD to the specified state
SYNOPSIS
--------
[verse]
-'git reset' [--mixed | --soft | --hard] [-q] [<commit>]
+'git reset' [--mixed | --soft | --hard | --merge] [-q] [<commit>]
'git reset' [-q] [<commit>] [--] <paths>...
DESCRIPTION
@@ -45,6 +45,11 @@ OPTIONS
switched to. Any changes to tracked files in the working tree
since <commit> are lost.
+--merge::
+ Resets the index to match the tree recorded by the named commit,
+ and updates the files that are different between the named commit
+ and the current commit in the working tree.
+
-q::
Be quiet, only report errors.
@@ -152,6 +157,28 @@ tip of the current branch in ORIG_HEAD, so resetting hard to it
brings your index file and the working tree back to that state,
and resets the tip of the branch to that commit.
+Undo a merge or pull inside a dirty work tree::
++
+------------
+$ git pull <1>
+Auto-merging nitfol
+Merge made by recursive.
+ nitfol | 20 +++++----
+ ...
+$ git reset --merge ORIG_HEAD <2>
+------------
++
+<1> Even if you may have local modifications in your
+working tree, you can safely say "git pull" when you know
+that the change in the other branch does not overlap with
+them.
+<2> After inspecting the result of the merge, you may find
+that the change in the other branch is unsatisfactory. Running
+"git reset --hard ORIG_HEAD" will let you go back to where you
+were, but it will discard your local changes, which you do not
+want. "git reset --merge" keeps your local changes.
+
+
Interrupted workflow::
+
Suppose you are interrupted by an urgent fix request while you
diff --git a/Documentation/technical/api-strbuf.txt b/Documentation/technical/api-strbuf.txt
index a8ee2fe6a..9a4e3ea92 100644
--- a/Documentation/technical/api-strbuf.txt
+++ b/Documentation/technical/api-strbuf.txt
@@ -133,8 +133,10 @@ Functions
* Adding data to the buffer
-NOTE: All of these functions in this section will grow the buffer as
- necessary.
+NOTE: All of the functions in this section will grow the buffer as necessary.
+If they fail for some reason other than memory shortage and the buffer hadn't
+been allocated before (i.e. the `struct strbuf` was set to `STRBUF_INIT`),
+then they will free() it.
`strbuf_addch`::
@@ -235,6 +237,11 @@ same behaviour as well.
Read the contents of a file, specified by its path. The third argument
can be used to give a hint about the file size, to avoid reallocs.
+`strbuf_readlink`::
+
+ Read the target of a symbolic link, specified by its path. The third
+ argument can be used to give a hint about the size, to avoid reallocs.
+
`strbuf_getline`::
Read a line from a FILE* pointer. The second argument specifies the line
diff --git a/INSTALL b/INSTALL
index d1deb0b3c..ae7f7508f 100644
--- a/INSTALL
+++ b/INSTALL
@@ -101,6 +101,9 @@ Issues of note:
Building and installing the info file additionally requires
makeinfo and docbook2X. Version 0.8.3 is known to work.
+ Building and installing the pdf file additionally requires
+ dblatex. Version 0.2.7 with asciidoc >= 8.2.7 is known to work.
+
The documentation is written for AsciiDoc 7, but "make
ASCIIDOC8=YesPlease doc" will let you format with AsciiDoc 8.
diff --git a/Makefile b/Makefile
index aabf0130b..2b873fa99 100644
--- a/Makefile
+++ b/Makefile
@@ -1307,6 +1307,9 @@ html:
info:
$(MAKE) -C Documentation info
+pdf:
+ $(MAKE) -C Documentation pdf
+
TAGS:
$(RM) TAGS
$(FIND) . -name '*.[hcS]' -print | xargs etags -a
@@ -1353,7 +1356,14 @@ endif
### Testing rules
-TEST_PROGRAMS = test-chmtime$X test-genrandom$X test-date$X test-delta$X test-sha1$X test-match-trees$X test-parse-options$X test-path-utils$X
+TEST_PROGRAMS += test-chmtime$X
+TEST_PROGRAMS += test-date$X
+TEST_PROGRAMS += test-delta$X
+TEST_PROGRAMS += test-genrandom$X
+TEST_PROGRAMS += test-match-trees$X
+TEST_PROGRAMS += test-parse-options$X
+TEST_PROGRAMS += test-path-utils$X
+TEST_PROGRAMS += test-sha1$X
all:: $(TEST_PROGRAMS)
@@ -1449,6 +1459,9 @@ install-html:
install-info:
$(MAKE) -C Documentation install-info
+install-pdf:
+ $(MAKE) -C Documentation install-pdf
+
quick-install-doc:
$(MAKE) -C Documentation quick-install
diff --git a/RelNotes b/RelNotes
index 2ebf251dd..ecced0873 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.1.1.txt \ No newline at end of file
+Documentation/RelNotes-1.6.2.txt \ No newline at end of file
diff --git a/builtin-apply.c b/builtin-apply.c
index 07244b073..a8f75ed3e 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -630,7 +630,7 @@ static int gitdiff_index(const char *line, struct patch *patch)
memcpy(patch->new_sha1_prefix, line, len);
patch->new_sha1_prefix[len] = 0;
if (*ptr == ' ')
- patch->new_mode = patch->old_mode = strtoul(ptr+1, NULL, 8);
+ patch->old_mode = strtoul(ptr+1, NULL, 8);
return 0;
}
@@ -2447,6 +2447,8 @@ static int check_preimage(struct patch *patch, struct cache_entry **ce, struct s
if (st_mode != patch->old_mode)
fprintf(stderr, "warning: %s has type %o, expected %o\n",
old_name, st_mode, patch->old_mode);
+ if (!patch->new_mode)
+ patch->new_mode = st_mode;
return 0;
is_new:
diff --git a/builtin-cat-file.c b/builtin-cat-file.c
index 30d00a666..8fad19dae 100644
--- a/builtin-cat-file.c
+++ b/builtin-cat-file.c
@@ -137,7 +137,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
break;
default:
- die("git cat-file: unknown option: %s\n", exp_type);
+ die("git cat-file: unknown option: %s", exp_type);
}
if (!buf)
diff --git a/builtin-clone.c b/builtin-clone.c
index 2feac9c5c..f1a1a0c36 100644
--- a/builtin-clone.c
+++ b/builtin-clone.c
@@ -192,15 +192,15 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
dir = opendir(src->buf);
if (!dir)
- die("failed to open %s\n", src->buf);
+ die("failed to open %s", src->buf);
if (mkdir(dest->buf, 0777)) {
if (errno != EEXIST)
- die("failed to create directory %s\n", dest->buf);
+ die("failed to create directory %s", dest->buf);
else if (stat(dest->buf, &buf))
- die("failed to stat %s\n", dest->buf);
+ die("failed to stat %s", dest->buf);
else if (!S_ISDIR(buf.st_mode))
- die("%s exists and is not a directory\n", dest->buf);
+ die("%s exists and is not a directory", dest->buf);
}
strbuf_addch(src, '/');
@@ -224,16 +224,16 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
}
if (unlink(dest->buf) && errno != ENOENT)
- die("failed to unlink %s\n", dest->buf);
+ die("failed to unlink %s", dest->buf);
if (!option_no_hardlinks) {
if (!link(src->buf, dest->buf))
continue;
if (option_local)
- die("failed to create link %s\n", dest->buf);
+ die("failed to create link %s", dest->buf);
option_no_hardlinks = 1;
}
if (copy_file(dest->buf, src->buf, 0666))
- die("failed to copy file to %s\n", dest->buf);
+ die("failed to copy file to %s", dest->buf);
}
closedir(dir);
}
diff --git a/builtin-fetch.c b/builtin-fetch.c
index 7568163af..de6f3074b 100644
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
@@ -607,7 +607,7 @@ static void set_option(const char *name, const char *value)
{
int r = transport_set_option(transport, name, value);
if (r < 0)
- die("Option \"%s\" value \"%s\" is not valid for %s\n",
+ die("Option \"%s\" value \"%s\" is not valid for %s",
name, value, transport->url);
if (r > 0)
warning("Option \"%s\" is ignored for %s\n",
diff --git a/builtin-init-db.c b/builtin-init-db.c
index d30c3fe2c..ee3911f8e 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -29,7 +29,7 @@ static void safe_create_dir(const char *dir, int share)
}
}
else if (share && adjust_shared_perm(dir))
- die("Could not make %s writable by group\n", dir);
+ die("Could not make %s writable by group", dir);
}
static void copy_templates_1(char *path, int baselen,
diff --git a/builtin-log.c b/builtin-log.c
index 99d1137b0..4a02ee987 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -249,22 +249,13 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
static void show_tagger(char *buf, int len, struct rev_info *rev)
{
- char *email_end, *p;
- unsigned long date;
- int tz;
+ struct strbuf out = STRBUF_INIT;
- email_end = memchr(buf, '>', len);
- if (!email_end)
- return;
- p = ++email_end;
- while (isspace(*p))
- p++;
- date = strtoul(p, &p, 10);
- while (isspace(*p))
- p++;
- tz = (int)strtol(p, NULL, 10);
- printf("Tagger: %.*s\nDate: %s\n", (int)(email_end - buf), buf,
- show_date(date, tz, rev->date_mode));
+ pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode,
+ git_log_output_encoding ?
+ git_log_output_encoding: git_commit_encoding);
+ printf("%s\n", out.buf);
+ strbuf_release(&out);
}
static int show_object(const unsigned char *sha1, int show_tag_object,
@@ -824,7 +815,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
committer = git_committer_info(IDENT_ERROR_ON_NO_NAME);
endpos = strchr(committer, '>');
if (!endpos)
- die("bogus committer info %s\n", committer);
+ die("bogus committer info %s", committer);
add_signoff = xmemdupz(committer, endpos - committer + 1);
}
else if (!strcmp(argv[i], "--attach")) {
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index e890f7a6d..f7c8c08b3 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -494,7 +494,7 @@ static void convert_to_utf8(struct strbuf *line, const char *charset)
return;
out = reencode_string(line->buf, metainfo_charset, charset);
if (!out)
- die("cannot convert from %s to %s\n",
+ die("cannot convert from %s to %s",
charset, metainfo_charset);
strbuf_attach(line, out, strlen(out), strlen(out));
}
diff --git a/builtin-merge-recursive.c b/builtin-merge-recursive.c
index 6b534c1a6..703045bfc 100644
--- a/builtin-merge-recursive.c
+++ b/builtin-merge-recursive.c
@@ -33,7 +33,7 @@ int cmd_merge_recursive(int argc, const char **argv, const char *prefix)
}
if (argc < 4)
- die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);
+ die("Usage: %s <base>... -- <head> <remote> ...", argv[0]);
for (i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "--"))
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index cedef52fd..e8515348b 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -78,7 +78,7 @@ static int progress = 1;
static int window = 10;
static uint32_t pack_size_limit, pack_size_limit_cfg;
static int depth = 50;
-static int delta_search_threads = 1;
+static int delta_search_threads;
static int pack_to_stdout;
static int num_preferred_base;
static struct progress *progress_state;
@@ -1612,11 +1612,18 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
find_deltas(list, &list_size, window, depth, processed);
return;
}
+ if (progress > pack_to_stdout)
+ fprintf(stderr, "Delta compression using %d threads.\n",
+ delta_search_threads);
/* Partition the work amongst work threads. */
for (i = 0; i < delta_search_threads; i++) {
unsigned sub_size = list_size / (delta_search_threads - i);
+ /* don't use too small segments or no deltas will be found */
+ if (sub_size < 2*window && i+1 < delta_search_threads)
+ sub_size = 0;
+
p[i].window = window;
p[i].depth = depth;
p[i].processed = processed;
diff --git a/builtin-reset.c b/builtin-reset.c
index 9514b77f8..c0cb915c2 100644
--- a/builtin-reset.c
+++ b/builtin-reset.c
@@ -20,11 +20,14 @@
#include "parse-options.h"
static const char * const git_reset_usage[] = {
- "git reset [--mixed | --soft | --hard] [-q] [<commit>]",
+ "git reset [--mixed | --soft | --hard | --merge] [-q] [<commit>]",
"git reset [--mixed] <commit> [--] <paths>...",
NULL
};
+enum reset_type { MIXED, SOFT, HARD, MERGE, NONE };
+static const char *reset_type_names[] = { "mixed", "soft", "hard", "merge", NULL };
+
static char *args_to_str(const char **argv)
{
char *buf = NULL;
@@ -49,7 +52,7 @@ static inline int is_merge(void)
return !access(git_path("MERGE_HEAD"), F_OK);
}
-static int reset_index_file(const unsigned char *sha1, int is_hard_reset, int quiet)
+static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet)
{
int i = 0;
const char *args[6];
@@ -57,9 +60,17 @@ static int reset_index_file(const unsigned char *sha1, int is_hard_reset, int qu
args[i++] = "read-tree";
if (!quiet)
args[i++] = "-v";
- args[i++] = "--reset";
- if (is_hard_reset)
+ switch (reset_type) {
+ case MERGE:
args[i++] = "-u";
+ args[i++] = "-m";
+ break;
+ case HARD:
+ args[i++] = "-u";
+ /* fallthrough */
+ default:
+ args[i++] = "--reset";
+ }
args[i++] = sha1_to_hex(sha1);
args[i] = NULL;
@@ -169,9 +180,6 @@ static void prepend_reflog_action(const char *action, char *buf, size_t size)
warning("Reflog action message too long: %.*s...", 50, buf);
}
-enum reset_type { MIXED, SOFT, HARD, NONE };
-static const char *reset_type_names[] = { "mixed", "soft", "hard", NULL };
-
int cmd_reset(int argc, const char **argv, const char *prefix)
{
int i = 0, reset_type = NONE, update_ref_status = 0, quiet = 0;
@@ -186,6 +194,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
OPT_SET_INT(0, "hard", &reset_type,
"reset HEAD, index and working tree", HARD),
+ OPT_SET_INT(0, "merge", &reset_type,
+ "reset HEAD, index and working tree", MERGE),
OPT_BOOLEAN('q', NULL, &quiet,
"disable showing new HEAD in hard reset and progress message"),
OPT_END()
@@ -266,7 +276,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
if (is_merge() || read_cache() < 0 || unmerged_cache())
die("Cannot do a soft reset in the middle of a merge.");
}
- else if (reset_index_file(sha1, (reset_type == HARD), quiet))
+ else if (reset_index_file(sha1, reset_type, quiet))
die("Could not reset index file to revision '%s'.", rev);
/* Any resets update HEAD to the head being switched to,
diff --git a/builtin-update-index.c b/builtin-update-index.c
index 65d577510..560497750 100644
--- a/builtin-update-index.c
+++ b/builtin-update-index.c
@@ -486,7 +486,7 @@ static int unresolve_one(const char *path)
static void read_head_pointers(void)
{
if (read_ref("HEAD", head_sha1))
- die("No HEAD -- no initial commit yet?\n");
+ die("No HEAD -- no initial commit yet?");
if (read_ref("MERGE_HEAD", merge_head_sha1)) {
fprintf(stderr, "Not in the middle of a merge.\n");
exit(0);
diff --git a/connect.c b/connect.c
index 2f55ad2c2..2f23ab3b8 100644
--- a/connect.c
+++ b/connect.c
@@ -315,7 +315,7 @@ static int git_tcp_connect_sock(char *host, int flags)
/* Not numeric */
struct servent *se = getservbyname(port,"tcp");
if ( !se )
- die("Unknown port %s\n", port);
+ die("Unknown port %s", port);
nport = se->s_port;
}
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index e00454983..7b074d798 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -563,7 +563,7 @@ _git_add ()
--*)
__gitcomp "
--interactive --refresh --patch --update --dry-run
- --ignore-errors
+ --ignore-errors --intent-to-add
"
return
esac
@@ -776,6 +776,7 @@ _git_diff ()
--no-ext-diff
--no-prefix --src-prefix= --dst-prefix=
--base --ours --theirs
+ --inter-hunk-context=
"
return
;;
@@ -967,6 +968,7 @@ _git_log ()
--color-words --walk-reflogs
--parents --children --full-history
--merge
+ --inter-hunk-context=
"
return
;;
@@ -1403,7 +1405,7 @@ _git_reset ()
local cur="${COMP_WORDS[COMP_CWORD]}"
case "$cur" in
--*)
- __gitcomp "--mixed --hard --soft"
+ __gitcomp "--merge --mixed --hard --soft"
return
;;
esac
@@ -1736,6 +1738,7 @@ _git ()
show) _git_show ;;
show-branch) _git_show_branch ;;
stash) _git_stash ;;
+ stage) _git_add ;;
submodule) _git_submodule ;;
svn) _git_svn ;;
tag) _git_tag ;;
diff --git a/daemon.c b/daemon.c
index 60bf6c743..540700ee8 100644
--- a/daemon.c
+++ b/daemon.c
@@ -716,7 +716,7 @@ static int socksetup(char *listen_addr, int listen_port, int **socklist_p)
gai = getaddrinfo(listen_addr, pbuf, &hints, &ai0);
if (gai)
- die("getaddrinfo() failed: %s\n", gai_strerror(gai));
+ die("getaddrinfo() failed: %s", gai_strerror(gai));
for (ai = ai0; ai; ai = ai->ai_next) {
int sockfd;
diff --git a/diff-lib.c b/diff-lib.c
index ae96c64ca..a41e1ec07 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -61,14 +61,12 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
int silent_on_removed = option & DIFF_SILENT_ON_REMOVED;
unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
? CE_MATCH_RACY_IS_DIRTY : 0);
- char symcache[PATH_MAX];
diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
if (diff_unmerged_stage < 0)
diff_unmerged_stage = 2;
entries = active_nr;
- symcache[0] = '\0';
for (i = 0; i < entries; i++) {
struct stat st;
unsigned int oldmode, newmode;
@@ -198,11 +196,6 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
* diff-index
*/
-struct oneway_unpack_data {
- struct rev_info *revs;
- char symcache[PATH_MAX];
-};
-
/* A file entry went away or appeared */
static void diff_index_show_file(struct rev_info *revs,
const char *prefix,
@@ -216,8 +209,7 @@ static void diff_index_show_file(struct rev_info *revs,
static int get_stat_data(struct cache_entry *ce,
const unsigned char **sha1p,
unsigned int *modep,
- int cached, int match_missing,
- struct oneway_unpack_data *cbdata)
+ int cached, int match_missing)
{
const unsigned char *sha1 = ce->sha1;
unsigned int mode = ce->ce_mode;
@@ -248,25 +240,24 @@ static int get_stat_data(struct cache_entry *ce,
return 0;
}
-static void show_new_file(struct oneway_unpack_data *cbdata,
+static void show_new_file(struct rev_info *revs,
struct cache_entry *new,
int cached, int match_missing)
{
const unsigned char *sha1;
unsigned int mode;
- struct rev_info *revs = cbdata->revs;
/*
* New file in the index: it might actually be different in
* the working copy.
*/
- if (get_stat_data(new, &sha1, &mode, cached, match_missing, cbdata) < 0)
+ if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0)
return;
diff_index_show_file(revs, "+", new, sha1, mode);
}
-static int show_modified(struct oneway_unpack_data *cbdata,
+static int show_modified(struct rev_info *revs,
struct cache_entry *old,
struct cache_entry *new,
int report_missing,
@@ -274,9 +265,8 @@ static int show_modified(struct oneway_unpack_data *cbdata,
{
unsigned int mode, oldmode;
const unsigned char *sha1;
- struct rev_info *revs = cbdata->revs;
- if (get_stat_data(new, &sha1, &mode, cached, match_missing, cbdata) < 0) {
+ if (get_stat_data(new, &sha1, &mode, cached, match_missing) < 0) {
if (report_missing)
diff_index_show_file(revs, "-", old,
old->sha1, old->ce_mode);
@@ -344,8 +334,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
struct cache_entry *idx,
struct cache_entry *tree)
{
- struct oneway_unpack_data *cbdata = o->unpack_data;
- struct rev_info *revs = cbdata->revs;
+ struct rev_info *revs = o->unpack_data;
int match_missing, cached;
/*
@@ -368,7 +357,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
* Something added to the tree?
*/
if (!tree) {
- show_new_file(cbdata, idx, cached, match_missing);
+ show_new_file(revs, idx, cached, match_missing);
return;
}
@@ -381,7 +370,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
}
/* Show difference between old and new */
- show_modified(cbdata, tree, idx, 1, cached, match_missing);
+ show_modified(revs, tree, idx, 1, cached, match_missing);
}
static inline void skip_same_name(struct cache_entry *ce, struct unpack_trees_options *o)
@@ -418,8 +407,7 @@ static int oneway_diff(struct cache_entry **src, struct unpack_trees_options *o)
{
struct cache_entry *idx = src[0];
struct cache_entry *tree = src[1];
- struct oneway_unpack_data *cbdata = o->unpack_data;
- struct rev_info *revs = cbdata->revs;
+ struct rev_info *revs = o->unpack_data;
if (idx && ce_stage(idx))
skip_same_name(idx, o);
@@ -446,7 +434,6 @@ int run_diff_index(struct rev_info *revs, int cached)
const char *tree_name;
struct unpack_trees_options opts;
struct tree_desc t;
- struct oneway_unpack_data unpack_cb;
mark_merge_entries();
@@ -456,14 +443,12 @@ int run_diff_index(struct rev_info *revs, int cached)
if (!tree)
return error("bad tree object %s", tree_name);
- unpack_cb.revs = revs;
- unpack_cb.symcache[0] = '\0';
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
opts.index_only = cached;
opts.merge = 1;
opts.fn = oneway_diff;
- opts.unpack_data = &unpack_cb;
+ opts.unpack_data = revs;
opts.src_index = &the_index;
opts.dst_index = NULL;
@@ -486,7 +471,6 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
struct cache_entry *last = NULL;
struct unpack_trees_options opts;
struct tree_desc t;
- struct oneway_unpack_data unpack_cb;
/*
* This is used by git-blame to run diff-cache internally;
@@ -515,14 +499,12 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
if (!tree)
die("bad tree object %s", sha1_to_hex(tree_sha1));
- unpack_cb.revs = &revs;
- unpack_cb.symcache[0] = '\0';
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
opts.index_only = 1;
opts.merge = 1;
opts.fn = oneway_diff;
- opts.unpack_data = &unpack_cb;
+ opts.unpack_data = &revs;
opts.src_index = &the_index;
opts.dst_index = &the_index;
diff --git a/diff.c b/diff.c
index 0484601f4..d23548292 100644
--- a/diff.c
+++ b/diff.c
@@ -1469,6 +1469,7 @@ static void builtin_diff(const char *name_a,
ecbdata.file = o->file;
xpp.flags = XDF_NEED_MINIMAL | o->xdl_opts;
xecfg.ctxlen = o->context;
+ xecfg.interhunkctxlen = o->interhunkcontext;
xecfg.flags = XDL_EMIT_FUNCNAMES;
if (pe)
xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
@@ -2039,7 +2040,7 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
if (lstat(one->path, &st) < 0)
die("stat %s", one->path);
if (index_path(one->sha1, one->path, &st, 0))
- die("cannot hash %s\n", one->path);
+ die("cannot hash %s", one->path);
}
}
else
@@ -2538,6 +2539,9 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->b_prefix = arg + 13;
else if (!strcmp(arg, "--no-prefix"))
options->a_prefix = options->b_prefix = "";
+ else if (opt_arg(arg, '\0', "inter-hunk-context",
+ &options->interhunkcontext))
+ ;
else if (!prefixcmp(arg, "--output=")) {
options->file = fopen(arg + strlen("--output="), "w");
options->close_file = 1;
diff --git a/diff.h b/diff.h
index 42582edee..4d5a32781 100644
--- a/diff.h
+++ b/diff.h
@@ -78,6 +78,7 @@ struct diff_options {
const char *a_prefix, *b_prefix;
unsigned flags;
int context;
+ int interhunkcontext;
int break_opt;
int detect_rename;
int skip_stat_unmatch;
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index b0223c341..ca60356d0 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -800,6 +800,7 @@ y - stage this hunk
n - do not stage this hunk
a - stage this and all the remaining hunks in the file
d - do not stage this hunk nor any of the remaining hunks in the file
+g - select a hunk to go to
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
@@ -836,6 +837,47 @@ sub patch_update_cmd {
}
}
+# Generate a one line summary of a hunk.
+sub summarize_hunk {
+ my $rhunk = shift;
+ my $summary = $rhunk->{TEXT}[0];
+
+ # Keep the line numbers, discard extra context.
+ $summary =~ s/@@(.*?)@@.*/$1 /s;
+ $summary .= " " x (20 - length $summary);
+
+ # Add some user context.
+ for my $line (@{$rhunk->{TEXT}}) {
+ if ($line =~ m/^[+-].*\w/) {
+ $summary .= $line;
+ last;
+ }
+ }
+
+ chomp $summary;
+ return substr($summary, 0, 80) . "\n";
+}
+
+
+# Print a one-line summary of each hunk in the array ref in
+# the first argument, starting wih the index in the 2nd.
+sub display_hunks {
+ my ($hunks, $i) = @_;
+ my $ctr = 0;
+ $i ||= 0;
+ for (; $i < @$hunks && $ctr < 20; $i++, $ctr++) {
+ my $status = " ";
+ if (defined $hunks->[$i]{USE}) {
+ $status = $hunks->[$i]{USE} ? "+" : "-";
+ }
+ printf "%s%2d: %s",
+ $status,
+ $i + 1,
+ summarize_hunk($hunks->[$i]);
+ }
+ return $i;
+}
+
sub patch_update_file {
my ($ix, $num);
my $path = shift;
@@ -904,6 +946,9 @@ sub patch_update_file {
if ($ix < $num - 1) {
$other .= '/J';
}
+ if ($num > 1) {
+ $other .= '/g';
+ }
for ($i = 0; $i < $num; $i++) {
if (!defined $hunk[$i]{USE}) {
$undecided = 1;
@@ -937,6 +982,28 @@ sub patch_update_file {
}
next;
}
+ elsif ($other =~ /g/ && $line =~ /^g(.*)/) {
+ my $response = $1;
+ my $no = $ix > 10 ? $ix - 10 : 0;
+ while ($response eq '') {
+ my $extra = "";
+ $no = display_hunks(\@hunk, $no);
+ if ($no < $num) {
+ $extra = " (<ret> to see more)";
+ }
+ print "go to which hunk$extra? ";
+ $response = <STDIN>;
+ chomp $response;
+ }
+ if ($response !~ /^\s*\d+\s*$/) {
+ print STDERR "Invalid number: '$response'\n";
+ } elsif (0 < $response && $response <= $num) {
+ $ix = $response - 1;
+ } else {
+ print STDERR "Sorry, only $num hunks available.\n";
+ }
+ next;
+ }
elsif ($line =~ /^d/i) {
while ($ix < $num) {
if (!defined $hunk[$ix]{USE}) {
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index c106f45af..eb62f719b 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -40,6 +40,16 @@ skip_commit()
done;
}
+# if you run 'git_commit_non_empty_tree "$@"' in a commit filter,
+# it will skip commits that leave the tree untouched, commit the other.
+git_commit_non_empty_tree()
+{
+ if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then
+ map "$3"
+ else
+ git commit-tree "$@"
+ fi
+}
# override die(): this version puts in an extra line break, so that
# the progress is still visible
@@ -109,11 +119,12 @@ filter_tree=
filter_index=
filter_parent=
filter_msg=cat
-filter_commit='git commit-tree "$@"'
+filter_commit=
filter_tag_name=
filter_subdir=
orig_namespace=refs/original/
force=
+prune_empty=
while :
do
case "$1" in
@@ -126,6 +137,11 @@ do
force=t
continue
;;
+ --prune-empty)
+ shift
+ prune_empty=t
+ continue
+ ;;
-*)
;;
*)
@@ -176,6 +192,17 @@ do
esac
done
+case "$prune_empty,$filter_commit" in
+,)
+ filter_commit='git commit-tree "$@"';;
+t,)
+ filter_commit="$functions;"' git_commit_non_empty_tree "$@"';;
+,*)
+ ;;
+*)
+ die "Cannot set --prune-empty and --filter-commit at the same time"
+esac
+
case "$force" in
t)
rm -rf "$tempdir"
diff --git a/git-mergetool.sh b/git-mergetool.sh
index d4078a6af..b2d53752a 100755
--- a/git-mergetool.sh
+++ b/git-mergetool.sh
@@ -8,7 +8,7 @@
# at the discretion of Junio C Hamano.
#
-USAGE='[--tool=tool] [file to merge] ...'
+USAGE='[--tool=tool] [-y|--no-prompt|--prompt] [file to merge] ...'
SUBDIRECTORY_OK=Yes
OPTIONS_SPEC=
. git-sh-setup
@@ -70,16 +70,16 @@ resolve_symlink_merge () {
git checkout-index -f --stage=2 -- "$MERGED"
git add -- "$MERGED"
cleanup_temp_files --save-backup
- return
+ return 0
;;
[rR]*)
git checkout-index -f --stage=3 -- "$MERGED"
git add -- "$MERGED"
cleanup_temp_files --save-backup
- return
+ return 0
;;
[aA]*)
- exit 1
+ return 1
;;
esac
done
@@ -97,15 +97,15 @@ resolve_deleted_merge () {
[mMcC]*)
git add -- "$MERGED"
cleanup_temp_files --save-backup
- return
+ return 0
;;
[dD]*)
git rm -- "$MERGED" > /dev/null
cleanup_temp_files
- return
+ return 0
;;
[aA]*)
- exit 1
+ return 1
;;
esac
done
@@ -137,7 +137,7 @@ merge_file () {
else
echo "$MERGED: file does not need merging"
fi
- exit 1
+ return 1
fi
ext="$$$(expr "$MERGED" : '.*\(\.[^/]*\)$')"
@@ -176,8 +176,10 @@ merge_file () {
echo "Normal merge conflict for '$MERGED':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
- printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
- read ans
+ if "$prompt" = true; then
+ printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
+ read ans
+ fi
case "$merge_tool" in
kdiff3)
@@ -267,7 +269,12 @@ merge_file () {
if test "$status" -ne 0; then
echo "merge of $MERGED failed" 1>&2
mv -- "$BACKUP" "$MERGED"
- exit 1
+
+ if test "$merge_keep_temporaries" = "false"; then
+ cleanup_temp_files
+ fi
+
+ return 1
fi
if test "$merge_keep_backup" = "true"; then
@@ -278,8 +285,11 @@ merge_file () {
git add -- "$MERGED"
cleanup_temp_files
+ return 0
}
+prompt=$(git config --bool mergetool.prompt || echo true)
+
while test $# != 0
do
case "$1" in
@@ -295,6 +305,12 @@ do
shift ;;
esac
;;
+ -y|--no-prompt)
+ prompt=false
+ ;;
+ --prompt)
+ prompt=true
+ ;;
--)
shift
break
@@ -341,6 +357,22 @@ init_merge_tool_path() {
fi
}
+prompt_after_failed_merge() {
+ while true; do
+ printf "Continue merging other unresolved paths (y/n) ? "
+ read ans
+ case "$ans" in
+
+ [yY]*)
+ return 0
+ ;;
+
+ [nN]*)
+ return 1
+ ;;
+ esac
+ done
+}
if test -z "$merge_tool"; then
merge_tool=`git config merge.tool`
@@ -389,6 +421,7 @@ else
init_merge_tool_path "$merge_tool"
merge_keep_backup="$(git config --bool merge.keepBackup || echo true)"
+ merge_keep_temporaries="$(git config --bool mergetool.keepTemporaries || echo false)"
if test -z "$merge_tool_cmd" && ! type "$merge_tool_path" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available as '$merge_tool_path'"
@@ -400,27 +433,44 @@ else
fi
fi
+last_status=0
+rollup_status=0
if test $# -eq 0 ; then
- files=`git ls-files -u | sed -e 's/^[^ ]* //' | sort -u`
- if test -z "$files" ; then
- echo "No files need merging"
- exit 0
+ files=`git ls-files -u | sed -e 's/^[^ ]* //' | sort -u`
+ if test -z "$files" ; then
+ echo "No files need merging"
+ exit 0
+ fi
+ echo Merging the files: "$files"
+ git ls-files -u |
+ sed -e 's/^[^ ]* //' |
+ sort -u |
+ while IFS= read i
+ do
+ if test $last_status -ne 0; then
+ prompt_after_failed_merge < /dev/tty || exit 1
fi
- echo Merging the files: "$files"
- git ls-files -u |
- sed -e 's/^[^ ]* //' |
- sort -u |
- while IFS= read i
- do
- printf "\n"
- merge_file "$i" < /dev/tty > /dev/tty
- done
+ printf "\n"
+ merge_file "$i" < /dev/tty > /dev/tty
+ last_status=$?
+ if test $last_status -ne 0; then
+ rollup_status=1
+ fi
+ done
else
- while test $# -gt 0; do
- printf "\n"
- merge_file "$1"
- shift
- done
+ while test $# -gt 0; do
+ if test $last_status -ne 0; then
+ prompt_after_failed_merge || exit 1
+ fi
+ printf "\n"
+ merge_file "$1"
+ last_status=$?
+ if test $last_status -ne 0; then
+ rollup_status=1
+ fi
+ shift
+ done
fi
-exit 0
+
+exit $rollup_status
diff --git a/git.c b/git.c
index 940a49896..a53e24fea 100644
--- a/git.c
+++ b/git.c
@@ -158,7 +158,7 @@ static int handle_alias(int *argcp, const char ***argv)
if (ret >= 0 && WIFEXITED(ret) &&
WEXITSTATUS(ret) != 127)
exit(WEXITSTATUS(ret));
- die("Failed to run '%s' when expanding alias '%s'\n",
+ die("Failed to run '%s' when expanding alias '%s'",
alias_string + 1, alias_command);
}
count = split_cmdline(alias_string, &new_argv);
@@ -428,9 +428,8 @@ int main(int argc, const char **argv)
* name, and the dirname as the default exec_path
* if we don't have anything better.
*/
- do
- --slash;
- while (cmd <= slash && !is_dir_sep(*slash));
+ while (cmd <= slash && !is_dir_sep(*slash))
+ slash--;
if (cmd <= slash) {
*slash++ = 0;
git_set_argv0_path(cmd);
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 99f71b47c..0ac84d1ad 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -203,7 +203,7 @@ our %feature = (
# $feature{'blame'}{'override'} = 1;
# and in project config gitweb.blame = 0|1;
'blame' => {
- 'sub' => \&feature_blame,
+ 'sub' => sub { feature_bool('blame', @_) },
'override' => 0,
'default' => [0]},
@@ -241,7 +241,7 @@ our %feature = (
# $feature{'grep'}{'override'} = 1;
# and in project config gitweb.grep = 0|1;
'grep' => {
- 'sub' => \&feature_grep,
+ 'sub' => sub { feature_bool('grep', @_) },
'override' => 0,
'default' => [1]},
@@ -255,7 +255,7 @@ our %feature = (
# $feature{'pickaxe'}{'override'} = 1;
# and in project config gitweb.pickaxe = 0|1;
'pickaxe' => {
- 'sub' => \&feature_pickaxe,
+ 'sub' => sub { feature_bool('pickaxe', @_) },
'override' => 0,
'default' => [1]},
@@ -363,16 +363,17 @@ sub gitweb_check_feature {
}
-sub feature_blame {
- my ($val) = git_get_project_config('blame', '--bool');
+sub feature_bool {
+ my $key = shift;
+ my ($val) = git_get_project_config($key, '--bool');
if ($val eq 'true') {
- return 1;
+ return (1);
} elsif ($val eq 'false') {
- return 0;
+ return (0);
}
- return $_[0];
+ return ($_[0]);
}
sub feature_snapshot {
@@ -387,30 +388,6 @@ sub feature_snapshot {
return @fmts;
}
-sub feature_grep {
- my ($val) = git_get_project_config('grep', '--bool');
-
- if ($val eq 'true') {
- return (1);
- } elsif ($val eq 'false') {
- return (0);
- }
-
- return ($_[0]);
-}
-
-sub feature_pickaxe {
- my ($val) = git_get_project_config('pickaxe', '--bool');
-
- if ($val eq 'true') {
- return (1);
- } elsif ($val eq 'false') {
- return (0);
- }
-
- return ($_[0]);
-}
-
# checking HEAD file with -e is fragile if the repository was
# initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed
# and then pruned.
@@ -4576,28 +4553,33 @@ sub git_tag {
}
sub git_blame {
- my $fd;
- my $ftype;
-
+ # permissions
gitweb_check_feature('blame')
- or die_error(403, "Blame view not allowed");
+ or die_error(403, "Blame view not allowed");
+ # error checking
die_error(400, "No file name given") unless $file_name;
$hash_base ||= git_get_head_hash($project);
- die_error(404, "Couldn't find base commit") unless ($hash_base);
+ die_error(404, "Couldn't find base commit") unless $hash_base;
my %co = parse_commit($hash_base)
or die_error(404, "Commit not found");
+ my $ftype = "blob";
if (!defined $hash) {
$hash = git_get_hash_by_path($hash_base, $file_name, "blob")
or die_error(404, "Error looking up file");
+ } else {
+ $ftype = git_get_type($hash);
+ if ($ftype !~ "blob") {
+ die_error(400, "Object is not a blob");
+ }
}
- $ftype = git_get_type($hash);
- if ($ftype !~ "blob") {
- die_error(400, "Object is not a blob");
- }
- open ($fd, "-|", git_cmd(), "blame", '-p', '--',
- $file_name, $hash_base)
+
+ # run git-blame --porcelain
+ open my $fd, "-|", git_cmd(), "blame", '-p',
+ $hash_base, '--', $file_name
or die_error(500, "Open git-blame failed");
+
+ # page header
git_header_html();
my $formats_nav =
$cgi->a({-href => href(action=>"blob", -replay=>1)},
@@ -4611,42 +4593,46 @@ sub git_blame {
git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
git_print_page_path($file_name, $ftype, $hash_base);
- my @rev_color = (qw(light2 dark2));
+
+ # page body
+ my @rev_color = qw(light2 dark2);
my $num_colors = scalar(@rev_color);
my $current_color = 0;
- my $last_rev;
+ my %metainfo = ();
+
print <<HTML;
<div class="page_body">
<table class="blame">
<tr><th>Commit</th><th>Line</th><th>Data</th></tr>
HTML
- my %metainfo = ();
- while (1) {
- $_ = <$fd>;
- last unless defined $_;
+ LINE:
+ while (my $line = <$fd>) {
+ chomp $line;
+ # the header: <SHA-1> <src lineno> <dst lineno> [<lines in group>]
+ # no <lines in group> for subsequent lines in group of lines
my ($full_rev, $orig_lineno, $lineno, $group_size) =
- /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/;
+ ($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
if (!exists $metainfo{$full_rev}) {
$metainfo{$full_rev} = {};
}
my $meta = $metainfo{$full_rev};
- while (<$fd>) {
- last if (s/^\t//);
- if (/^(\S+) (.*)$/) {
+ my $data;
+ while ($data = <$fd>) {
+ chomp $data;
+ last if ($data =~ s/^\t//); # contents of line
+ if ($data =~ /^(\S+) (.*)$/) {
$meta->{$1} = $2;
}
}
- my $data = $_;
- chomp $data;
- my $rev = substr($full_rev, 0, 8);
+ my $short_rev = substr($full_rev, 0, 8);
my $author = $meta->{'author'};
- my %date = parse_date($meta->{'author-time'},
- $meta->{'author-tz'});
+ my %date =
+ parse_date($meta->{'author-time'}, $meta->{'author-tz'});
my $date = $date{'iso-tz'};
if ($group_size) {
- $current_color = ++$current_color % $num_colors;
+ $current_color = ($current_color + 1) % $num_colors;
}
- print "<tr class=\"$rev_color[$current_color]\">\n";
+ print "<tr id=\"l$lineno\" class=\"$rev_color[$current_color]\">\n";
if ($group_size) {
print "<td class=\"sha1\"";
print " title=\"". esc_html($author) . ", $date\"";
@@ -4655,20 +4641,25 @@ HTML
print $cgi->a({-href => href(action=>"commit",
hash=>$full_rev,
file_name=>$file_name)},
- esc_html($rev));
+ esc_html($short_rev));
print "</td>\n";
}
- open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
- or die_error(500, "Open git-rev-parse failed");
- my $parent_commit = <$dd>;
- close $dd;
- chomp($parent_commit);
+ my $parent_commit;
+ if (!exists $meta->{'parent'}) {
+ open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
+ or die_error(500, "Open git-rev-parse failed");
+ $parent_commit = <$dd>;
+ close $dd;
+ chomp($parent_commit);
+ $meta->{'parent'} = $parent_commit;
+ } else {
+ $parent_commit = $meta->{'parent'};
+ }
my $blamed = href(action => 'blame',
file_name => $meta->{'filename'},
hash_base => $parent_commit);
print "<td class=\"linenr\">";
print $cgi->a({ -href => "$blamed#l$orig_lineno",
- -id => "l$lineno",
-class => "linenr" },
esc_html($lineno));
print "</td>";
@@ -4679,6 +4670,8 @@ HTML
print "</div>";
close $fd
or print "Reading blob failed\n";
+
+ # page footer
git_footer_html();
}
@@ -6146,8 +6139,8 @@ XML
}
my $path = esc_html(chop_str($proj{'path'}, 25, 5));
- my $rss = "$my_url?p=$proj{'path'};a=rss";
- my $html = "$my_url?p=$proj{'path'};a=summary";
+ my $rss = href('project' => $proj{'path'}, 'action' => 'rss', -full => 1);
+ my $html = href('project' => $proj{'path'}, 'action' => 'summary', -full => 1);
print "<outline type=\"rss\" text=\"$path\" title=\"$path\" xmlUrl=\"$rss\" htmlUrl=\"$html\"/>\n";
}
print <<XML;
diff --git a/grep.c b/grep.c
index 600f69f2f..49e931996 100644
--- a/grep.c
+++ b/grep.c
@@ -395,7 +395,7 @@ static int match_expr_eval(struct grep_opt *o,
h |= match_expr_eval(o, x->u.binary.right, bol, eol, ctx, 1);
break;
default:
- die("Unexpected node type (internal error) %d\n", x->node);
+ die("Unexpected node type (internal error) %d", x->node);
}
if (collect_hits)
x->hit |= h;
diff --git a/http-push.c b/http-push.c
index 7c6460919..a4b7d0866 100644
--- a/http-push.c
+++ b/http-push.c
@@ -87,6 +87,7 @@ static struct object_list *objects;
struct repo
{
char *url;
+ char *path;
int path_len;
int has_info_refs;
int can_update_info_refs;
@@ -1424,9 +1425,19 @@ static void handle_remote_ls_ctx(struct xml_ctx *ctx, int tag_closed)
ls->userFunc(ls);
}
} else if (!strcmp(ctx->name, DAV_PROPFIND_NAME) && ctx->cdata) {
- ls->dentry_name = xmalloc(strlen(ctx->cdata) -
+ char *path = ctx->cdata;
+ if (*ctx->cdata == 'h') {
+ path = strstr(path, "//");
+ if (path) {
+ path = strchr(path+2, '/');
+ }
+ }
+ if (path) {
+ path += remote->path_len;
+ }
+ ls->dentry_name = xmalloc(strlen(path) -
remote->path_len + 1);
- strcpy(ls->dentry_name, ctx->cdata + remote->path_len);
+ strcpy(ls->dentry_name, path + remote->path_len);
} else if (!strcmp(ctx->name, DAV_PROPFIND_COLLECTION)) {
ls->dentry_flags |= IS_DIR;
}
@@ -2206,10 +2217,11 @@ int main(int argc, char **argv)
if (!remote->url) {
char *path = strstr(arg, "//");
remote->url = arg;
+ remote->path_len = strlen(arg);
if (path) {
- path = strchr(path+2, '/');
- if (path)
- remote->path_len = strlen(path);
+ remote->path = strchr(path+2, '/');
+ if (remote->path)
+ remote->path_len = strlen(remote->path);
}
continue;
}
@@ -2238,8 +2250,9 @@ int main(int argc, char **argv)
rewritten_url = xmalloc(strlen(remote->url)+2);
strcpy(rewritten_url, remote->url);
strcat(rewritten_url, "/");
+ remote->path = rewritten_url + (remote->path - remote->url);
+ remote->path_len++;
remote->url = rewritten_url;
- ++remote->path_len;
}
/* Verify DAV compliance/lock support */
diff --git a/imap-send.c b/imap-send.c
index 3703dbd1a..c3fa0df85 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -115,9 +115,9 @@ static int nfvasprintf(char **strp, const char *fmt, va_list ap)
len = vsnprintf(tmp, sizeof(tmp), fmt, ap);
if (len < 0)
- die("Fatal: Out of memory\n");
+ die("Fatal: Out of memory");
if (len >= sizeof(tmp))
- die("imap command overflow !\n");
+ die("imap command overflow!");
*strp = xmemdupz(tmp, len);
return len;
}
@@ -482,7 +482,7 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
va_start(va, fmt);
if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
- die("Fatal: buffer too small. Please report a bug.\n");
+ die("Fatal: buffer too small. Please report a bug.");
va_end(va);
return ret;
}
diff --git a/index-pack.c b/index-pack.c
index 60ed41a99..2931511e8 100644
--- a/index-pack.c
+++ b/index-pack.c
@@ -178,7 +178,7 @@ static char *open_pack_file(char *pack_name)
} else
output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
if (output_fd < 0)
- die("unable to create %s: %s\n", pack_name, strerror(errno));
+ die("unable to create %s: %s", pack_name, strerror(errno));
pack_fd = output_fd;
} else {
input_fd = open(pack_name, O_RDONLY);
diff --git a/merge-recursive.c b/merge-recursive.c
index a0c804c81..b97026bd5 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -447,6 +447,30 @@ static void flush_buffer(int fd, const char *buf, unsigned long size)
}
}
+static int would_lose_untracked(const char *path)
+{
+ int pos = cache_name_pos(path, strlen(path));
+
+ if (pos < 0)
+ pos = -1 - pos;
+ while (pos < active_nr &&
+ !strcmp(path, active_cache[pos]->name)) {
+ /*
+ * If stage #0, it is definitely tracked.
+ * If it has stage #2 then it was tracked
+ * before this merge started. All other
+ * cases the path was not tracked.
+ */
+ switch (ce_stage(active_cache[pos])) {
+ case 0:
+ case 2:
+ return 0;
+ }
+ pos++;
+ }
+ return file_exists(path);
+}
+
static int make_room_for_path(const char *path)
{
int status;
@@ -462,6 +486,14 @@ static int make_room_for_path(const char *path)
die(msg, path, "");
}
+ /*
+ * Do not unlink a file in the work tree if we are not
+ * tracking it.
+ */
+ if (would_lose_untracked(path))
+ return error("refusing to lose untracked file at '%s'",
+ path);
+
/* Successful unlink is good.. */
if (!unlink(path))
return 0;
@@ -902,6 +934,11 @@ static int process_renames(struct merge_options *o,
ren1_src, ren1_dst, branch1,
branch2);
update_file(o, 0, ren1->pair->two->sha1, ren1->pair->two->mode, ren1_dst);
+ update_stages(ren1_dst, NULL,
+ branch1 == o->branch1 ?
+ ren1->pair->two : NULL,
+ branch1 == o->branch1 ?
+ NULL : ren1->pair->two, 1);
} else if (!sha_eq(dst_other.sha1, null_sha1)) {
const char *new_path;
clean_merge = 0;
diff --git a/pack-redundant.c b/pack-redundant.c
index 25b81a445..e93eb966e 100644
--- a/pack-redundant.c
+++ b/pack-redundant.c
@@ -463,7 +463,7 @@ static void minimize(struct pack_list **min)
pll_free(perm_all);
}
if (perm_ok == NULL)
- die("Internal error: No complete sets found!\n");
+ die("Internal error: No complete sets found!");
/* find the permutation with the smallest size */
perm = perm_ok;
@@ -573,14 +573,14 @@ static struct pack_list * add_pack_file(char *filename)
struct packed_git *p = packed_git;
if (strlen(filename) < 40)
- die("Bad pack filename: %s\n", filename);
+ die("Bad pack filename: %s", filename);
while (p) {
if (strstr(p->pack_name, filename))
return add_pack(p);
p = p->next;
}
- die("Filename %s not found in packed_git\n", filename);
+ die("Filename %s not found in packed_git", filename);
}
static void load_all(void)
@@ -636,7 +636,7 @@ int main(int argc, char **argv)
add_pack_file(*(argv + i++));
if (local_packs == NULL)
- die("Zero packs found!\n");
+ die("Zero packs found!");
load_all_objects();
diff --git a/strbuf.c b/strbuf.c
index bdf49544d..6ed06840b 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -256,18 +256,21 @@ size_t strbuf_expand_dict_cb(struct strbuf *sb, const char *placeholder,
size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
{
size_t res;
+ size_t oldalloc = sb->alloc;
strbuf_grow(sb, size);
res = fread(sb->buf + sb->len, 1, size, f);
- if (res > 0) {
+ if (res > 0)
strbuf_setlen(sb, sb->len + res);
- }
+ else if (res < 0 && oldalloc == 0)
+ strbuf_release(sb);
return res;
}
ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
{
size_t oldlen = sb->len;
+ size_t oldalloc = sb->alloc;
strbuf_grow(sb, hint ? hint : 8192);
for (;;) {
@@ -275,7 +278,10 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
cnt = xread(fd, sb->buf + sb->len, sb->alloc - sb->len - 1);
if (cnt < 0) {
- strbuf_setlen(sb, oldlen);
+ if (oldalloc == 0)
+ strbuf_release(sb);
+ else
+ strbuf_setlen(sb, oldlen);
return -1;
}
if (!cnt)
@@ -292,6 +298,8 @@ ssize_t strbuf_read(struct strbuf *sb, int fd, size_t hint)
int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
{
+ size_t oldalloc = sb->alloc;
+
if (hint < 32)
hint = 32;
@@ -311,7 +319,8 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
/* .. the buffer was too small - try again */
hint *= 2;
}
- strbuf_release(sb);
+ if (oldalloc == 0)
+ strbuf_release(sb);
return -1;
}
diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh
new file mode 100755
index 000000000..e4e3e28fc
--- /dev/null
+++ b/t/t4032-diff-inter-hunk-context.sh
@@ -0,0 +1,92 @@
+#!/bin/sh
+
+test_description='diff hunk fusing'
+
+. ./test-lib.sh
+
+f() {
+ echo $1
+ i=1
+ while test $i -le $2
+ do
+ echo $i
+ i=$(expr $i + 1)
+ done
+ echo $3
+}
+
+t() {
+ case $# in
+ 4) hunks=$4; cmd="diff -U$3";;
+ 5) hunks=$5; cmd="diff -U$3 --inter-hunk-context=$4";;
+ esac
+ label="$cmd, $1 common $2"
+ file=f$1
+ expected=expected.$file.$3.$hunks
+
+ if ! test -f $file
+ then
+ f A $1 B >$file
+ git add $file
+ git commit -q -m. $file
+ f X $1 Y >$file
+ fi
+
+ test_expect_success "$label: count hunks ($hunks)" "
+ test $(git $cmd $file | grep '^@@ ' | wc -l) = $hunks
+ "
+
+ test -f $expected &&
+ test_expect_success "$label: check output" "
+ git $cmd $file | grep -v '^index ' >actual &&
+ test_cmp $expected actual
+ "
+}
+
+cat <<EOF >expected.f1.0.1 || exit 1
+diff --git a/f1 b/f1
+--- a/f1
++++ b/f1
+@@ -1,3 +1,3 @@
+-A
++X
+ 1
+-B
++Y
+EOF
+
+cat <<EOF >expected.f1.0.2 || exit 1
+diff --git a/f1 b/f1
+--- a/f1
++++ b/f1
+@@ -1 +1 @@
+-A
++X
+@@ -3 +3 @@ A
+-B
++Y
+EOF
+
+# common lines ctx intrctx hunks
+t 1 line 0 2
+t 1 line 0 0 2
+t 1 line 0 1 1
+t 1 line 0 2 1
+t 1 line 1 1
+
+t 2 lines 0 2
+t 2 lines 0 0 2
+t 2 lines 0 1 2
+t 2 lines 0 2 1
+t 2 lines 1 1
+
+t 3 lines 1 2
+t 3 lines 1 0 2
+t 3 lines 1 1 1
+t 3 lines 1 2 1
+
+t 9 lines 3 2
+t 9 lines 3 2 2
+t 9 lines 3 3 1
+
+test_done
diff --git a/t/t4129-apply-samemode.sh b/t/t4129-apply-samemode.sh
new file mode 100755
index 000000000..adfcbb5a3
--- /dev/null
+++ b/t/t4129-apply-samemode.sh
@@ -0,0 +1,62 @@
+#!/bin/sh
+
+test_description='applying patch with mode bits'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo original >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ git tag initial &&
+ echo modified >file &&
+ git diff --stat -p >patch-0.txt &&
+ chmod +x file &&
+ git diff --stat -p >patch-1.txt
+'
+
+test_expect_success 'same mode (no index)' '
+ git reset --hard &&
+ chmod +x file &&
+ git apply patch-0.txt &&
+ test -x file
+'
+
+test_expect_success 'same mode (with index)' '
+ git reset --hard &&
+ chmod +x file &&
+ git add file &&
+ git apply --index patch-0.txt &&
+ test -x file &&
+ git diff --exit-code
+'
+
+test_expect_success 'same mode (index only)' '
+ git reset --hard &&
+ chmod +x file &&
+ git add file &&
+ git apply --cached patch-0.txt &&
+ git ls-files -s file | grep "^100755"
+'
+
+test_expect_success 'mode update (no index)' '
+ git reset --hard &&
+ git apply patch-1.txt &&
+ test -x file
+'
+
+test_expect_success 'mode update (with index)' '
+ git reset --hard &&
+ git apply --index patch-1.txt &&
+ test -x file &&
+ git diff --exit-code
+'
+
+test_expect_success 'mode update (index only)' '
+ git reset --hard &&
+ git apply --cached patch-1.txt &&
+ git ls-files -s file | grep "^100755"
+'
+
+test_done
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index 884e24253..e6f70d474 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -10,6 +10,7 @@ test_expect_success \
'setup' \
'rm -rf .git
git init &&
+ git config pack.threads 1 &&
i=1 &&
while test $i -le 100
do
diff --git a/t/t6024-recursive-merge.sh b/t/t6024-recursive-merge.sh
index 802d0d06e..129fa3000 100755
--- a/t/t6024-recursive-merge.sh
+++ b/t/t6024-recursive-merge.sh
@@ -97,4 +97,27 @@ test_expect_success 'refuse to merge binary files' '
merge.err
'
+test_expect_success 'mark rename/delete as unmerged' '
+
+ git reset --hard &&
+ git checkout -b delete &&
+ git rm a1 &&
+ test_tick &&
+ git commit -m delete &&
+ git checkout -b rename HEAD^ &&
+ git mv a1 a2
+ test_tick &&
+ git commit -m rename &&
+ test_must_fail git merge delete &&
+ test 1 = $(git ls-files --unmerged | wc -l) &&
+ git rev-parse --verify :2:a2 &&
+ test_must_fail git rev-parse --verify :3:a2 &&
+ git checkout -f delete &&
+ test_must_fail git merge rename &&
+ test 1 = $(git ls-files --unmerged | wc -l) &&
+ test_must_fail git rev-parse --verify :2:a2 &&
+ git rev-parse --verify :3:a2
+
+'
+
test_done
diff --git a/t/t7003-filter-branch.sh b/t/t7003-filter-branch.sh
index b0a9d7d53..8537bf916 100755
--- a/t/t7003-filter-branch.sh
+++ b/t/t7003-filter-branch.sh
@@ -262,4 +262,12 @@ test_expect_success 'Tag name filtering allows slashes in tag names' '
test_cmp expect actual
'
+test_expect_success 'Prune empty commits' '
+ git rev-list HEAD > expect &&
+ make_commit to_remove &&
+ git filter-branch -f --index-filter "git update-index --remove to_remove" --prune-empty HEAD &&
+ git rev-list HEAD > actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh
index 6e18a9631..5998baf27 100755
--- a/t/t7500-commit.sh
+++ b/t/t7500-commit.sh
@@ -149,10 +149,7 @@ EOF
test_expect_success '--signoff' '
echo "yet another content *narf*" >> foo &&
- echo "zort" | (
- test_set_editor "$TEST_DIRECTORY"/t7500/add-content &&
- git commit -s -F - foo
- ) &&
+ echo "zort" | git commit -s -F - foo &&
git cat-file commit HEAD | sed "1,/^$/d" > output &&
test_cmp expect output
'
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 63bfc6d8b..b4e2b4db8 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -127,6 +127,26 @@ test_expect_success \
"showing committed revisions" \
"git rev-list HEAD >current"
+cat >editor <<\EOF
+#!/bin/sh
+sed -e "s/good/bad/g" < "$1" > "$1-"
+mv "$1-" "$1"
+EOF
+chmod 755 editor
+
+cat >msg <<EOF
+A good commit message.
+EOF
+
+test_expect_success \
+ 'editor not invoked if -F is given' '
+ echo "moo" >file &&
+ VISUAL=./editor git commit -a -F msg &&
+ git show -s --pretty=format:"%s" | grep -q good &&
+ echo "quack" >file &&
+ echo "Another good message." | VISUAL=./editor git commit -a -F - &&
+ git show -s --pretty=format:"%s" | grep -q good
+ '
# We could just check the head sha1, but checking each commit makes it
# easier to isolate bugs.
diff --git a/t/t7607-merge-overwrite.sh b/t/t7607-merge-overwrite.sh
new file mode 100755
index 000000000..49f4e1599
--- /dev/null
+++ b/t/t7607-merge-overwrite.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+test_description='git-merge
+
+Do not overwrite changes.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ echo c0 > c0.c &&
+ git add c0.c &&
+ git commit -m c0 &&
+ git tag c0 &&
+ echo c1 > c1.c &&
+ git add c1.c &&
+ git commit -m c1 &&
+ git tag c1 &&
+ git reset --hard c0 &&
+ echo c2 > c2.c &&
+ git add c2.c &&
+ git commit -m c2 &&
+ git tag c2 &&
+ git reset --hard c1 &&
+ echo "c1 a" > c1.c &&
+ git add c1.c &&
+ git commit -m "c1 a" &&
+ git tag c1a &&
+ echo "VERY IMPORTANT CHANGES" > important
+'
+
+test_expect_success 'will not overwrite untracked file' '
+ git reset --hard c1 &&
+ cat important > c2.c &&
+ ! git merge c2 &&
+ test_cmp important c2.c
+'
+
+test_expect_success 'will not overwrite new file' '
+ git reset --hard c1 &&
+ cat important > c2.c &&
+ git add c2.c &&
+ ! git merge c2 &&
+ test_cmp important c2.c
+'
+
+test_expect_success 'will not overwrite staged changes' '
+ git reset --hard c1 &&
+ cat important > c2.c &&
+ git add c2.c &&
+ rm c2.c &&
+ ! git merge c2 &&
+ git checkout c2.c &&
+ test_cmp important c2.c
+'
+
+test_expect_success 'will not overwrite removed file' '
+ git reset --hard c1 &&
+ git rm c1.c &&
+ git commit -m "rm c1.c" &&
+ cat important > c1.c &&
+ ! git merge c1a &&
+ test_cmp important c1.c
+'
+
+test_expect_success 'will not overwrite re-added file' '
+ git reset --hard c1 &&
+ git rm c1.c &&
+ git commit -m "rm c1.c" &&
+ cat important > c1.c &&
+ git add c1.c &&
+ ! git merge c1a &&
+ test_cmp important c1.c
+'
+
+test_expect_success 'will not overwrite removed file with staged changes' '
+ git reset --hard c1 &&
+ git rm c1.c &&
+ git commit -m "rm c1.c" &&
+ cat important > c1.c &&
+ git add c1.c &&
+ rm c1.c &&
+ ! git merge c1a &&
+ git checkout c1.c &&
+ test_cmp important c1.c
+'
+
+test_done
diff --git a/t/t9130-git-svn-authors-file.sh b/t/t9130-git-svn-authors-file.sh
new file mode 100755
index 000000000..b8fb27756
--- /dev/null
+++ b/t/t9130-git-svn-authors-file.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Eric Wong
+#
+
+test_description='git svn authors file tests'
+
+. ./lib-git-svn.sh
+
+cat > svn-authors <<EOF
+aa = AAAAAAA AAAAAAA <aa@example.com>
+bb = BBBBBBB BBBBBBB <bb@example.com>
+EOF
+
+test_expect_success 'setup svnrepo' '
+ for i in aa bb cc dd
+ do
+ svn mkdir -m $i --username $i "$svnrepo"/$i
+ done
+ '
+
+test_expect_success 'start import with incomplete authors file' '
+ ! git svn clone --authors-file=svn-authors "$svnrepo" x
+ '
+
+test_expect_success 'imported 2 revisions successfully' '
+ (
+ cd x
+ test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 2 &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn | \
+ grep "^author BBBBBBB BBBBBBB <bb@example\.com> " &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn~1 | \
+ grep "^author AAAAAAA AAAAAAA <aa@example\.com> "
+ )
+ '
+
+cat >> svn-authors <<EOF
+cc = CCCCCCC CCCCCCC <cc@example.com>
+dd = DDDDDDD DDDDDDD <dd@example.com>
+EOF
+
+test_expect_success 'continues to import once authors have been added' '
+ (
+ cd x
+ git svn fetch --authors-file=../svn-authors &&
+ test "`git rev-list refs/remotes/git-svn | wc -l`" -eq 4 &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn | \
+ grep "^author DDDDDDD DDDDDDD <dd@example\.com> " &&
+ git rev-list -1 --pretty=raw refs/remotes/git-svn~1 | \
+ grep "^author CCCCCCC CCCCCCC <cc@example\.com> "
+ )
+ '
+
+test_expect_success 'authors-file against globs' '
+ svn mkdir -m globs --username aa \
+ "$svnrepo"/aa/trunk "$svnrepo"/aa/branches "$svnrepo"/aa/tags &&
+ git svn clone --authors-file=svn-authors -s "$svnrepo"/aa aa-work &&
+ for i in bb ee cc
+ do
+ branch="aa/branches/$i"
+ svn mkdir -m "$branch" --username $i "$svnrepo/$branch"
+ done
+ '
+
+test_expect_success 'fetch fails on ee' '
+ ( cd aa-work && ! git svn fetch --authors-file=../svn-authors )
+ '
+
+tmp_config_get () {
+ GIT_CONFIG=.git/svn/.metadata git config --get "$1"
+}
+
+test_expect_success 'failure happened without negative side effects' '
+ (
+ cd aa-work &&
+ test 6 -eq "`tmp_config_get svn-remote.svn.branches-maxRev`" &&
+ test 6 -eq "`tmp_config_get svn-remote.svn.tags-maxRev`"
+ )
+ '
+
+cat >> svn-authors <<EOF
+ee = EEEEEEE EEEEEEE <ee@example.com>
+EOF
+
+test_expect_success 'fetch continues after authors-file is fixed' '
+ (
+ cd aa-work &&
+ git svn fetch --authors-file=../svn-authors &&
+ test 8 -eq "`tmp_config_get svn-remote.svn.branches-maxRev`" &&
+ test 8 -eq "`tmp_config_get svn-remote.svn.tags-maxRev`"
+ )
+ '
+
+test_done
diff --git a/xdiff/xdiff.h b/xdiff/xdiff.h
index 84fff583e..361f80231 100644
--- a/xdiff/xdiff.h
+++ b/xdiff/xdiff.h
@@ -84,6 +84,7 @@ typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long
typedef struct s_xdemitconf {
long ctxlen;
+ long interhunkctxlen;
unsigned long flags;
find_func_t find_func;
void *find_func_priv;
diff --git a/xdiff/xemit.c b/xdiff/xemit.c
index 4625c1b42..05bfa41f1 100644
--- a/xdiff/xemit.c
+++ b/xdiff/xemit.c
@@ -59,9 +59,10 @@ static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *
*/
xdchange_t *xdl_get_hunk(xdchange_t *xscr, xdemitconf_t const *xecfg) {
xdchange_t *xch, *xchp;
+ long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
for (xchp = xscr, xch = xscr->next; xch; xchp = xch, xch = xch->next)
- if (xch->i1 - (xchp->i1 + xchp->chg1) > 2 * xecfg->ctxlen)
+ if (xch->i1 - (xchp->i1 + xchp->chg1) > max_common)
break;
return xchp;