summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Documentation/RelNotes-1.6.4.1.txt46
-rw-r--r--Documentation/config.txt8
-rw-r--r--Documentation/git-am.txt5
-rw-r--r--Documentation/git-apply.txt13
-rw-r--r--Documentation/git-branch.txt6
-rw-r--r--Documentation/git-clone.txt12
-rw-r--r--Documentation/git-filter-branch.txt10
-rw-r--r--Documentation/git-instaweb.txt2
-rw-r--r--Documentation/git-log.txt8
-rw-r--r--Documentation/git-rebase.txt3
-rw-r--r--Documentation/git-replace.txt71
-rw-r--r--Documentation/git-submodule.txt13
-rw-r--r--Documentation/git-svn.txt6
-rw-r--r--Documentation/git-tag.txt20
-rw-r--r--Documentation/git-verify-pack.txt8
-rw-r--r--Documentation/git-write-tree.txt3
-rw-r--r--Documentation/git.txt3
-rw-r--r--Makefile5
-rw-r--r--abspath.c6
-rw-r--r--bisect.c2
-rw-r--r--builtin-apply.c173
-rw-r--r--builtin-fsck.c1
-rw-r--r--builtin-log.c16
-rw-r--r--builtin-mailinfo.c8
-rw-r--r--builtin-mailsplit.c45
-rw-r--r--builtin-pack-objects.c2
-rw-r--r--builtin-prune.c1
-rw-r--r--builtin-read-tree.c10
-rw-r--r--builtin-replace.c159
-rw-r--r--builtin-unpack-objects.c18
-rw-r--r--builtin-verify-pack.c49
-rw-r--r--builtin.h2
-rw-r--r--cache.h10
-rw-r--r--commit.c10
-rw-r--r--commit.h2
-rw-r--r--compat/snprintf.c3
-rwxr-xr-xcontrib/completion/git-completion.bash3
-rwxr-xr-xcontrib/fast-import/git-p4169
-rw-r--r--diff-lib.c6
-rw-r--r--entry.c12
-rw-r--r--environment.c2
-rwxr-xr-xgit-add--interactive.perl4
-rwxr-xr-xgit-am.sh12
-rwxr-xr-xgit-bisect.sh2
-rw-r--r--git-compat-util.h2
-rwxr-xr-xgit-filter-branch.sh12
-rwxr-xr-xgit-instaweb.sh57
-rwxr-xr-xgit-rebase.sh3
-rwxr-xr-xgit-stash.sh27
-rwxr-xr-xgit-submodule.sh19
-rwxr-xr-xgit-svn.perl11
-rw-r--r--git.c1
-rw-r--r--gitweb/INSTALL9
-rwxr-xr-xgitweb/gitweb.perl21
-rw-r--r--graph.c14
-rw-r--r--http.c4
-rw-r--r--log-tree.c7
-rw-r--r--log-tree.h2
-rw-r--r--mktag.c7
-rw-r--r--object.c9
-rw-r--r--parse-options.c8
-rw-r--r--parse-options.h4
-rw-r--r--pretty.c2
-rw-r--r--refs.c5
-rw-r--r--refs.h1
-rw-r--r--replace_object.c114
-rw-r--r--revision.c2
-rw-r--r--revision.h3
-rw-r--r--sha1_file.c23
-rw-r--r--strbuf.c15
-rw-r--r--strbuf.h1
-rwxr-xr-xt/t1009-read-tree-new-index.sh25
-rwxr-xr-xt/t2000-checkout-cache-clash.sh9
-rwxr-xr-xt/t3400-rebase.sh26
-rwxr-xr-xt/t3701-add-interactive.sh11
-rwxr-xr-xt/t4013-diff-various.sh1
-rw-r--r--t/t4013/diff.log_--decorate=full_--all34
-rwxr-xr-xt/t4039-diff-assume-unchanged.sh31
-rwxr-xr-xt/t4107-apply-ignore-whitespace.sh185
-rw-r--r--t/t5531-deep-submodule-push.sh35
-rwxr-xr-xt/t6030-bisect-porcelain.sh18
-rwxr-xr-xt/t6050-replace.sh200
-rwxr-xr-xt/t7401-submodule-summary.sh22
-rwxr-xr-xt/t7408-submodule-reference.sh (renamed from t/t7406-submodule-reference.sh)0
-rwxr-xr-xt/t9120-git-svn-clone-with-percent-escapes.sh52
-rw-r--r--upload-pack.c1
87 files changed, 1751 insertions, 252 deletions
diff --git a/.gitignore b/.gitignore
index 41c0b20a76..10808e3a73 100644
--- a/.gitignore
+++ b/.gitignore
@@ -105,6 +105,7 @@ git-reflog
git-relink
git-remote
git-repack
+git-replace
git-repo-config
git-request-pull
git-rerere
diff --git a/Documentation/RelNotes-1.6.4.1.txt b/Documentation/RelNotes-1.6.4.1.txt
new file mode 100644
index 0000000000..e439e45b96
--- /dev/null
+++ b/Documentation/RelNotes-1.6.4.1.txt
@@ -0,0 +1,46 @@
+GIT v1.6.4.1 Release Notes
+==========================
+
+Fixes since v1.6.4
+------------------
+
+ * An unquoted value in the configuration file, when it contains more than
+ one whitespaces in a row, got them replaced with a single space.
+
+ * "git am" used to accept a single piece of e-mail per file (not a mbox)
+ as its input, but multiple input format support in v1.6.4 broke it.
+ Apparently many people have been depending on this feature.
+
+ * The short help text for "git filter-branch" command was a single long
+ line, wrapped by terminals, and was hard to read.
+
+ * The "recursive" strategy of "git merge" segfaulted when a merge has
+ more than one merge-bases, and merging of these merge-bases involves
+ a rename/rename or a rename/add conflict.
+
+ * "git pull --rebase" did not use the right fork point when the
+ repository has already fetched from the upstream that rewinds the
+ branch it is based on in an earlier fetch.
+
+ * Explain the concept of fast-forward more fully in "git push"
+ documentation, and hint to refer to it from an error message when the
+ command refuses an update to protect the user.
+
+ * The default value for pack.deltacachesize, used by "git repack", is now
+ 256M, instead of unbounded. Otherwise a repack of a moderately sized
+ repository would needlessly eat into swap.
+
+ * Document how "git repack" (hence "git gc") interacts with a repository
+ that borrows its objects from other repositories (e.g. ones created by
+ "git clone -s").
+
+ * "git show" on an annotated tag lacked a delimiting blank line between
+ the tag itself and the contents of the object it tags.
+
+ * "git verify-pack -v" erroneously reported number of objects with too
+ deep delta depths as "chain length 0" objects.
+
+ * Long names of authors and committers outside US-ASCII were sometimes
+ incorrectly shown in "gitweb".
+
+Other minor documentation updates are included.
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 2632c5149e..5256c7fb81 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -461,6 +461,14 @@ it will be treated as a shell command. For example, defining
executed from the top-level directory of a repository, which may
not necessarily be the current directory.
+apply.ignorewhitespace::
+ When set to 'change', tells 'git-apply' to ignore changes in
+ whitespace, in the same way as the '--ignore-space-change'
+ option.
+ When set to one of: no, none, never, false tells 'git-apply' to
+ respect all whitespace differences.
+ See linkgit:git-apply[1].
+
apply.whitespace::
Tells 'git-apply' how to handle whitespaces, in the same way
as the '--whitespace' option. See linkgit:git-apply[1].
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 32e689b2bf..fcacc94650 100644
--- a/Documentation/git-am.txt
+++ b/Documentation/git-am.txt
@@ -11,7 +11,7 @@ SYNOPSIS
[verse]
'git am' [--signoff] [--keep] [--utf8 | --no-utf8]
[--3way] [--interactive] [--committer-date-is-author-date]
- [--ignore-date]
+ [--ignore-date] [--ignore-space-change | --ignore-whitespace]
[--whitespace=<option>] [-C<n>] [-p<n>] [--directory=<dir>]
[--reject] [-q | --quiet]
[<mbox> | <Maildir>...]
@@ -65,6 +65,9 @@ default. You can use `--no-utf8` to override this.
it is supposed to apply to and we have those blobs
available locally.
+--ignore-date::
+--ignore-space-change::
+--ignore-whitespace::
--whitespace=<option>::
-C<n>::
-p<n>::
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
index 735374d7df..5ee8c91f2d 100644
--- a/Documentation/git-apply.txt
+++ b/Documentation/git-apply.txt
@@ -13,6 +13,7 @@ SYNOPSIS
[--apply] [--no-add] [--build-fake-ancestor=<file>] [-R | --reverse]
[--allow-binary-replacement | --binary] [--reject] [-z]
[-pNUM] [-CNUM] [--inaccurate-eof] [--recount] [--cached]
+ [--ignore-space-change | --ignore-whitespace ]
[--whitespace=<nowarn|warn|fix|error|error-all>]
[--exclude=PATH] [--include=PATH] [--directory=<root>]
[--verbose] [<patch>...]
@@ -149,6 +150,14 @@ patch to each path is used. A patch to a path that does not match any
include/exclude pattern is used by default if there is no include pattern
on the command line, and ignored if there is any include pattern.
+--ignore-space-change::
+--ignore-whitespace::
+ When applying a patch, ignore changes in whitespace in context
+ lines if necessary.
+ Context lines will preserve their whitespace, and they will not
+ undergo whitespace fixing regardless of the value of the
+ `--whitespace` option. New lines will still be fixed, though.
+
--whitespace=<action>::
When applying a patch, detect a new or modified line that has
whitespace errors. What are considered whitespace errors is
@@ -205,6 +214,10 @@ running `git apply --directory=modules/git-gui`.
Configuration
-------------
+apply.ignorewhitespace::
+ Set to 'change' if you want changes in whitespace to be ignored by default.
+ Set to one of: no, none, never, false if you want changes in
+ whitespace to be significant.
apply.whitespace::
When no `--whitespace` flag is given from the command
line, this configuration item is used as the default.
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index ae201deb7a..99988872eb 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -209,6 +209,12 @@ but different purposes:
- `--no-merged` is used to find branches which are candidates for merging
into HEAD, since those branches are not fully contained by HEAD.
+SEE ALSO
+--------
+linkgit:git-check-ref-format[1],
+linkgit:git-fetch[1],
+linkgit:git-remote[1].
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org> and Junio C Hamano <gitster@pobox.com>
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 5a685ceec9..88ea272ee5 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -72,8 +72,16 @@ These objects may be removed by normal git operations (such as 'git-commit')
which automatically call `git gc --auto`. (See linkgit:git-gc[1].)
If these objects are removed and were referenced by the cloned repository,
then the cloned repository will become corrupt.
-
-
++
+Note that running `git repack` without the `-l` option in a repository
+cloned with `-s` will copy objects from the source repository into a pack
+in the cloned repository, removing the disk space savings of `clone -s`.
+It is safe, however, to run `git gc`, which uses the `-l` option by
+default.
++
+If you want to break the dependency of a repository cloned with `-s` on
+its source repository, you can simply run `git repack -a` to copy all
+objects from the source repository into a pack in the cloned repository.
--reference <repository>::
If the reference repository is on the local machine
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index ab527b5b31..32ea8564a5 100644
--- a/Documentation/git-filter-branch.txt
+++ b/Documentation/git-filter-branch.txt
@@ -305,6 +305,16 @@ range in addition to the new branch name. The new branch name will
point to the top-most revision that a 'git-rev-list' of this range
will print.
+If you need to add 'Acked-by' lines to, say, the last 10 commits (none
+of which is a merge), use this command:
+
+--------------------------------------------------------
+git filter-branch --msg-filter '
+ cat &&
+ echo "Acked-by: Bugs Bunny <bunny@bugzilla.org>"
+' HEAD~10..HEAD
+--------------------------------------------------------
+
*NOTE* the changes introduced by the commits, and which are not reverted
by subsequent commits, will still be in the rewritten branch. If you want
to throw out _changes_ together with the commits, you should use the
diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt
index 22da21a54f..0771f25443 100644
--- a/Documentation/git-instaweb.txt
+++ b/Documentation/git-instaweb.txt
@@ -29,7 +29,7 @@ OPTIONS
The HTTP daemon command-line that will be executed.
Command-line options may be specified here, and the
configuration file will be added at the end of the command-line.
- Currently lighttpd, apache2 and webrick are supported.
+ Currently apache2, lighttpd, mongoose and webrick are supported.
(Default: lighttpd)
-m::
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 34cf4e5811..3d79de11ec 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -37,8 +37,12 @@ include::diff-options.txt[]
and <until>, see "SPECIFYING REVISIONS" section in
linkgit:git-rev-parse[1].
---decorate::
- Print out the ref names of any commits that are shown.
+--decorate[=short|full]::
+ Print out the ref names of any commits that are shown. If 'short' is
+ specified, the ref name prefixes 'refs/heads/', 'refs/tags/' and
+ 'refs/remotes/' will not be printed. If 'full' is specified, the
+ full ref name (including prefix) will be printed. The default option
+ is 'short'.
--source::
Print out the ref name given on the command line by which each
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index db1b71d248..0aefc34d0d 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -268,8 +268,9 @@ OPTIONS
exit with the message "Current branch is up to date" in such a
situation.
+--ignore-whitespace::
--whitespace=<option>::
- This flag is passed to the 'git-apply' program
+ These flag are passed to the 'git-apply' program
(see linkgit:git-apply[1]) that applies the patch.
Incompatible with the --interactive option.
diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt
new file mode 100644
index 0000000000..915cb77b29
--- /dev/null
+++ b/Documentation/git-replace.txt
@@ -0,0 +1,71 @@
+git-replace(1)
+==============
+
+NAME
+----
+git-replace - Create, list, delete refs to replace objects
+
+SYNOPSIS
+--------
+[verse]
+'git replace' [-f] <object> <replacement>
+'git replace' -d <object>...
+'git replace' -l [<pattern>]
+
+DESCRIPTION
+-----------
+Adds a 'replace' reference in `.git/refs/replace/`
+
+The name of the 'replace' reference is the SHA1 of the object that is
+replaced. The content of the replace reference is the SHA1 of the
+replacement object.
+
+Unless `-f` is given, the replace reference must not yet exist in
+`.git/refs/replace/` directory.
+
+OPTIONS
+-------
+-f::
+ If an existing replace ref for the same object exists, it will
+ be overwritten (instead of failing).
+
+-d::
+ Delete existing replace refs for the given objects.
+
+-l <pattern>::
+ List replace refs for objects that match the given pattern (or
+ all if no pattern is given).
+ Typing "git replace" without arguments, also lists all replace
+ refs.
+
+BUGS
+----
+Comparing blobs or trees that have been replaced with those that
+replace them will not work properly. And using 'git reset --hard' to
+go back to a replaced commit will move the branch to the replacement
+commit instead of the replaced commit.
+
+There may be other problems when using 'git rev-list' related to
+pending objects. And of course things may break if an object of one
+type is replaced by an object of another type (for example a blob
+replaced by a commit).
+
+SEE ALSO
+--------
+linkgit:git-tag[1]
+linkgit:git-branch[1]
+
+Author
+------
+Written by Christian Couder <chriscool@tuxfamily.org> and Junio C
+Hamano <gitster@pobox.com>, based on 'git tag' by Kristian Hogsberg
+<krh@redhat.com> and Carlos Rica <jasampler@gmail.com>.
+
+Documentation
+--------------
+Documentation by Christian Couder <chriscool@tuxfamily.org> and the
+git-list <git@vger.kernel.org>, based on 'git tag' documentation.
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index b81c830c28..5ccdd18c89 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -15,7 +15,7 @@ SYNOPSIS
'git submodule' [--quiet] init [--] [<path>...]
'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase]
[--reference <repository>] [--merge] [--recursive] [--] [<path>...]
-'git submodule' [--quiet] summary [--cached] [--summary-limit <n>] [commit] [--] [<path>...]
+'git submodule' [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
'git submodule' [--quiet] foreach [--recursive] <command>
'git submodule' [--quiet] sync [--] [<path>...]
@@ -133,7 +133,11 @@ summary::
Show commit summary between the given commit (defaults to HEAD) and
working tree/index. For a submodule in question, a series of commits
in the submodule between the given super project commit and the
- index or working tree (switched by --cached) are shown.
+ index or working tree (switched by --cached) are shown. If the option
+ --files is given, show the series of commits in the submodule between
+ the index of the super project and the working tree of the submodule
+ (this option doesn't allow to use the --cached option or to provide an
+ explicit commit).
foreach::
Evaluates an arbitrary shell command in each checked out submodule.
@@ -178,6 +182,11 @@ OPTIONS
commands typically use the commit found in the submodule HEAD, but
with this option, the commit stored in the index is used instead.
+--files::
+ This option is only valid for the summary command. This command
+ compares the commit in the index with that in the submodule HEAD
+ when this option is used.
+
-n::
--summary-limit::
This option is only valid for the summary command.
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 22a0389f1e..1812890a7e 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -102,9 +102,6 @@ COMMANDS
Store Git commit times in the local timezone instead of UTC. This
makes 'git log' (even without --date=local) show the same times
that `svn log` would in the local timezone.
-
---parent;;
- Fetch only from the SVN parent of the current HEAD.
+
This doesn't interfere with interoperating with the Subversion
repository you cloned from, but if you wish for your local Git
@@ -112,6 +109,9 @@ repository to be able to interoperate with someone else's local Git
repository, either don't use this option or you should both use it in
the same local timezone.
+--parent;;
+ Fetch only from the SVN parent of the current HEAD.
+
--ignore-paths=<regex>;;
This allows one to specify a Perl regular expression that will
cause skipping of all matching paths from checkout from SVN.
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 1118ce22dc..5113eaec62 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -10,17 +10,15 @@ SYNOPSIS
--------
[verse]
'git tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>]
- <name> [<commit> | <object>]
-'git tag' -d <name>...
+ <tagname> [<commit> | <object>]
+'git tag' -d <tagname>...
'git tag' [-n[<num>]] -l [--contains <commit>] [<pattern>]
-'git tag' -v <name>...
+'git tag' -v <tagname>...
DESCRIPTION
-----------
-Adds a 'tag' reference in `.git/refs/tags/`. The tag <name> must pass
-linkgit:git-check-ref-format[1] which basicly means that control characters,
-space, ~, ^, :, ?, *, [ and \ are prohibited.
+Adds a tag reference in `.git/refs/tags/`.
Unless `-f` is given, the tag must not yet exist in
`.git/refs/tags/` directory.
@@ -88,6 +86,12 @@ OPTIONS
Implies `-a` if none of `-a`, `-s`, or `-u <key-id>`
is given.
+<tagname>::
+ The name of the tag to create, delete, or describe.
+ The new tag name must pass all checks defined by
+ linkgit:git-check-ref-format[1]. Some of these checks
+ may restrict the characters allowed in a tag name.
+
CONFIGURATION
-------------
By default, 'git-tag' in sign-with-default mode (-s) will use your
@@ -252,6 +256,10 @@ $ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1
------------
+SEE ALSO
+--------
+linkgit:git-check-ref-format[1].
+
Author
------
Written by Linus Torvalds <torvalds@osdl.org>,
diff --git a/Documentation/git-verify-pack.txt b/Documentation/git-verify-pack.txt
index d791a80819..97f7f9165e 100644
--- a/Documentation/git-verify-pack.txt
+++ b/Documentation/git-verify-pack.txt
@@ -25,7 +25,13 @@ OPTIONS
-v::
--verbose::
After verifying the pack, show list of objects contained
- in the pack.
+ in the pack and a histogram of delta chain length.
+
+-s::
+--stat-only::
+ Do not verify the pack contents; only show the histogram of delta
+ chain length. With `--verbose`, list of objects is also shown.
+
\--::
Do not interpret any more arguments as options.
diff --git a/Documentation/git-write-tree.txt b/Documentation/git-write-tree.txt
index 26d3850e73..c8899d528a 100644
--- a/Documentation/git-write-tree.txt
+++ b/Documentation/git-write-tree.txt
@@ -12,7 +12,8 @@ SYNOPSIS
DESCRIPTION
-----------
-Creates a tree object using the current index.
+Creates a tree object using the current index. The name of the new
+tree object is printed to standard output.
The index must be in a fully merged state.
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 5832c752e1..a9bacfbef4 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,9 +43,10 @@ unreleased) version of git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.6.4/git.html[documentation for release 1.6.4]
+* link:v1.6.4.1/git.html[documentation for release 1.6.4.1]
* release notes for
+ link:RelNotes-1.6.4.1.txt[1.6.4.1],
link:RelNotes-1.6.4.txt[1.6.4].
* link:v1.6.3.4/git.html[documentation for release 1.6.3.4]
diff --git a/Makefile b/Makefile
index e6df8ecde6..a9a4d89667 100644
--- a/Makefile
+++ b/Makefile
@@ -536,6 +536,7 @@ LIB_OBJS += read-cache.o
LIB_OBJS += reflog-walk.o
LIB_OBJS += refs.o
LIB_OBJS += remote.o
+LIB_OBJS += replace_object.o
LIB_OBJS += rerere.o
LIB_OBJS += revision.o
LIB_OBJS += run-command.o
@@ -625,6 +626,7 @@ BUILTIN_OBJS += builtin-read-tree.o
BUILTIN_OBJS += builtin-receive-pack.o
BUILTIN_OBJS += builtin-reflog.o
BUILTIN_OBJS += builtin-remote.o
+BUILTIN_OBJS += builtin-replace.o
BUILTIN_OBJS += builtin-rerere.o
BUILTIN_OBJS += builtin-reset.o
BUILTIN_OBJS += builtin-rev-list.o
@@ -755,9 +757,6 @@ ifeq ($(uname_S),SunOS)
NO_C99_FORMAT = YesPlease
NO_STRTOUMAX = YesPlease
endif
- ifdef NO_IPV6
- NEEDS_RESOLV = YesPlease
- endif
INSTALL = /usr/ucb/install
TAR = gtar
BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H
diff --git a/abspath.c b/abspath.c
index 4bee0ba1ec..b88122cbe7 100644
--- a/abspath.c
+++ b/abspath.c
@@ -18,7 +18,7 @@ const char *make_absolute_path(const char *path)
{
static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
char cwd[1024] = "";
- int buf_index = 1, len;
+ int buf_index = 1;
int depth = MAXDEPTH;
char *last_elem = NULL;
@@ -50,7 +50,7 @@ const char *make_absolute_path(const char *path)
die_errno ("Could not get current working directory");
if (last_elem) {
- int len = strlen(buf);
+ size_t len = strlen(buf);
if (len + strlen(last_elem) + 2 > PATH_MAX)
die ("Too long path name: '%s/%s'",
buf, last_elem);
@@ -61,7 +61,7 @@ const char *make_absolute_path(const char *path)
}
if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
- len = readlink(buf, next_buf, PATH_MAX);
+ ssize_t len = readlink(buf, next_buf, PATH_MAX);
if (len < 0)
die_errno ("Invalid symlink '%s'", buf);
if (PATH_MAX <= len)
diff --git a/bisect.c b/bisect.c
index 7f20acb4b9..dc18db8af9 100644
--- a/bisect.c
+++ b/bisect.c
@@ -991,7 +991,7 @@ int bisect_next_all(const char *prefix)
if (!hashcmp(bisect_rev, current_bad_sha1)) {
exit_if_skipped_commits(tried, current_bad_sha1);
- printf("%s is first bad commit\n", bisect_rev_hex);
+ printf("%s is the first bad commit\n", bisect_rev_hex);
show_diff_tree(prefix, revs.commits->item);
/* This means the bisection process succeeded. */
exit(10);
diff --git a/builtin-apply.c b/builtin-apply.c
index 39dc96ae02..ae11b41ef2 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -61,6 +61,13 @@ static enum ws_error_action {
static int whitespace_error;
static int squelch_whitespace_errors = 5;
static int applied_after_fixing_ws;
+
+static enum ws_ignore {
+ ignore_ws_none,
+ ignore_ws_change,
+} ws_ignore_action = ignore_ws_none;
+
+
static const char *patch_input_file;
static const char *root;
static int root_len;
@@ -97,6 +104,21 @@ static void parse_whitespace_option(const char *option)
die("unrecognized whitespace option '%s'", option);
}
+static void parse_ignorewhitespace_option(const char *option)
+{
+ if (!option || !strcmp(option, "no") ||
+ !strcmp(option, "false") || !strcmp(option, "never") ||
+ !strcmp(option, "none")) {
+ ws_ignore_action = ignore_ws_none;
+ return;
+ }
+ if (!strcmp(option, "change")) {
+ ws_ignore_action = ignore_ws_change;
+ return;
+ }
+ die("unrecognized whitespace ignore option '%s'", option);
+}
+
static void set_default_whitespace_mode(const char *whitespace_option)
{
if (!whitespace_option && !apply_default_whitespace)
@@ -214,6 +236,62 @@ static uint32_t hash_line(const char *cp, size_t len)
return h;
}
+/*
+ * Compare lines s1 of length n1 and s2 of length n2, ignoring
+ * whitespace difference. Returns 1 if they match, 0 otherwise
+ */
+static int fuzzy_matchlines(const char *s1, size_t n1,
+ const char *s2, size_t n2)
+{
+ const char *last1 = s1 + n1 - 1;
+ const char *last2 = s2 + n2 - 1;
+ int result = 0;
+
+ if (n1 < 0 || n2 < 0)
+ return 0;
+
+ /* ignore line endings */
+ while ((*last1 == '\r') || (*last1 == '\n'))
+ last1--;
+ while ((*last2 == '\r') || (*last2 == '\n'))
+ last2--;
+
+ /* skip leading whitespace */
+ while (isspace(*s1) && (s1 <= last1))
+ s1++;
+ while (isspace(*s2) && (s2 <= last2))
+ s2++;
+ /* early return if both lines are empty */
+ if ((s1 > last1) && (s2 > last2))
+ return 1;
+ while (!result) {
+ result = *s1++ - *s2++;
+ /*
+ * Skip whitespace inside. We check for whitespace on
+ * both buffers because we don't want "a b" to match
+ * "ab"
+ */
+ if (isspace(*s1) && isspace(*s2)) {
+ while (isspace(*s1) && s1 <= last1)
+ s1++;
+ while (isspace(*s2) && s2 <= last2)
+ s2++;
+ }
+ /*
+ * If we reached the end on one side only,
+ * lines don't match
+ */
+ if (
+ ((s2 > last2) && (s1 <= last1)) ||
+ ((s1 > last1) && (s2 <= last2)))
+ return 0;
+ if ((s1 > last1) && (s2 > last2))
+ break;
+ }
+
+ return !result;
+}
+
static void add_line_info(struct image *img, const char *bol, size_t len, unsigned flag)
{
ALLOC_GROW(img->line_allocated, img->nr + 1, img->alloc);
@@ -1672,10 +1750,17 @@ static int read_old_data(struct stat *st, const char *path, struct strbuf *buf)
}
}
+/*
+ * Update the preimage, and the common lines in postimage,
+ * from buffer buf of length len. If postlen is 0 the postimage
+ * is updated in place, otherwise it's updated on a new buffer
+ * of length postlen
+ */
+
static void update_pre_post_images(struct image *preimage,
struct image *postimage,
char *buf,
- size_t len)
+ size_t len, size_t postlen)
{
int i, ctx;
char *new, *old, *fixed;
@@ -1694,11 +1779,19 @@ static void update_pre_post_images(struct image *preimage,
*preimage = fixed_preimage;
/*
- * Adjust the common context lines in postimage, in place.
- * This is possible because whitespace fixing does not make
- * the string grow.
+ * Adjust the common context lines in postimage. This can be
+ * done in-place when we are just doing whitespace fixing,
+ * which does not make the string grow, but needs a new buffer
+ * when ignoring whitespace causes the update, since in this case
+ * we could have e.g. tabs converted to multiple spaces.
+ * We trust the caller to tell us if the update can be done
+ * in place (postlen==0) or not.
*/
- new = old = postimage->buf;
+ old = postimage->buf;
+ if (postlen)
+ new = postimage->buf = xmalloc(postlen);
+ else
+ new = old;
fixed = preimage->buf;
for (i = ctx = 0; i < postimage->nr; i++) {
size_t len = postimage->line[i].len;
@@ -1773,12 +1866,58 @@ static int match_fragment(struct image *img,
!memcmp(img->buf + try, preimage->buf, preimage->len))
return 1;
+ /*
+ * No exact match. If we are ignoring whitespace, run a line-by-line
+ * fuzzy matching. We collect all the line length information because
+ * we need it to adjust whitespace if we match.
+ */
+ if (ws_ignore_action == ignore_ws_change) {
+ size_t imgoff = 0;
+ size_t preoff = 0;
+ size_t postlen = postimage->len;
+ size_t imglen[preimage->nr];
+ for (i = 0; i < preimage->nr; i++) {
+ size_t prelen = preimage->line[i].len;
+
+ imglen[i] = img->line[try_lno+i].len;
+ if (!fuzzy_matchlines(
+ img->buf + try + imgoff, imglen[i],
+ preimage->buf + preoff, prelen))
+ return 0;
+ if (preimage->line[i].flag & LINE_COMMON)
+ postlen += imglen[i] - prelen;
+ imgoff += imglen[i];
+ preoff += prelen;
+ }
+
+ /*
+ * Ok, the preimage matches with whitespace fuzz. Update it and
+ * the common postimage lines to use the same whitespace as the
+ * target. imgoff now holds the true length of the target that
+ * matches the preimage, and we need to update the line lengths
+ * of the preimage to match the target ones.
+ */
+ fixed_buf = xmalloc(imgoff);
+ memcpy(fixed_buf, img->buf + try, imgoff);
+ for (i = 0; i < preimage->nr; i++)
+ preimage->line[i].len = imglen[i];
+
+ /*
+ * Update the preimage buffer and the postimage context lines.
+ */
+ update_pre_post_images(preimage, postimage,
+ fixed_buf, imgoff, postlen);
+ return 1;
+ }
+
if (ws_error_action != correct_ws_error)
return 0;
/*
* The hunk does not apply byte-by-byte, but the hash says
- * it might with whitespace fuzz.
+ * it might with whitespace fuzz. We haven't been asked to
+ * ignore whitespace, we were asked to correct whitespace
+ * errors, so let's try matching after whitespace correction.
*/
fixed_buf = xmalloc(preimage->len + 1);
buf = fixed_buf;
@@ -1830,7 +1969,7 @@ static int match_fragment(struct image *img,
* hunk match. Update the context lines in the postimage.
*/
update_pre_post_images(preimage, postimage,
- fixed_buf, buf - fixed_buf);
+ fixed_buf, buf - fixed_buf, 0);
return 1;
unmatch_exit:
@@ -3272,6 +3411,8 @@ static int git_apply_config(const char *var, const char *value, void *cb)
{
if (!strcmp(var, "apply.whitespace"))
return git_config_string(&apply_default_whitespace, var, value);
+ else if (!strcmp(var, "apply.ignorewhitespace"))
+ return git_config_string(&apply_default_ignorewhitespace, var, value);
return git_default_config(var, value, cb);
}
@@ -3308,6 +3449,16 @@ static int option_parse_z(const struct option *opt,
return 0;
}
+static int option_parse_space_change(const struct option *opt,
+ const char *arg, int unset)
+{
+ if (unset)
+ ws_ignore_action = ignore_ws_none;
+ else
+ ws_ignore_action = ignore_ws_change;
+ return 0;
+}
+
static int option_parse_whitespace(const struct option *opt,
const char *arg, int unset)
{
@@ -3384,6 +3535,12 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
{ OPTION_CALLBACK, 0, "whitespace", &whitespace_option, "action",
"detect new or modified lines that have whitespace errors",
0, option_parse_whitespace },
+ { OPTION_CALLBACK, 0, "ignore-space-change", NULL, NULL,
+ "ignore changes in whitespace when finding context",
+ PARSE_OPT_NOARG, option_parse_space_change },
+ { OPTION_CALLBACK, 0, "ignore-whitespace", NULL, NULL,
+ "ignore changes in whitespace when finding context",
+ PARSE_OPT_NOARG, option_parse_space_change },
OPT_BOOLEAN('R', "reverse", &apply_in_reverse,
"apply the patch in reverse"),
OPT_BOOLEAN(0, "unidiff-zero", &unidiff_zero,
@@ -3408,6 +3565,8 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
git_config(git_apply_config, NULL);
if (apply_default_whitespace)
parse_whitespace_option(apply_default_whitespace);
+ if (apply_default_ignorewhitespace)
+ parse_ignorewhitespace_option(apply_default_ignorewhitespace);
argc = parse_options(argc, argv, prefix, builtin_apply_options,
apply_usage, 0);
diff --git a/builtin-fsck.c b/builtin-fsck.c
index b3d38fa277..c58b0e337e 100644
--- a/builtin-fsck.c
+++ b/builtin-fsck.c
@@ -589,6 +589,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
struct alternate_object_database *alt;
errors_found = 0;
+ read_replace_refs = 0;
argc = parse_options(argc, argv, prefix, fsck_opts, fsck_usage, 0);
if (write_lost_and_found) {
diff --git a/builtin-log.c b/builtin-log.c
index 82236c531b..25e21ed415 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -35,6 +35,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
struct rev_info *rev)
{
int i;
+ int decoration_style = 0;
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
@@ -61,8 +62,15 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (!strcmp(arg, "--decorate")) {
- load_ref_decorations();
- rev->show_decorations = 1;
+ decoration_style = DECORATE_SHORT_REFS;
+ } else if (!prefixcmp(arg, "--decorate=")) {
+ const char *v = skip_prefix(arg, "--decorate=");
+ if (!strcmp(v, "full"))
+ decoration_style = DECORATE_FULL_REFS;
+ else if (!strcmp(v, "short"))
+ decoration_style = DECORATE_SHORT_REFS;
+ else
+ die("invalid --decorate option: %s", arg);
} else if (!strcmp(arg, "--source")) {
rev->show_source = 1;
} else if (!strcmp(arg, "-h")) {
@@ -70,6 +78,10 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
} else
die("unrecognized argument: %s", arg);
}
+ if (decoration_style) {
+ rev->show_decorations = 1;
+ load_ref_decorations(decoration_style);
+ }
}
/*
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 92637ac0ba..b0b5d8f6cb 100644
--- a/builtin-mailinfo.c
+++ b/builtin-mailinfo.c
@@ -765,7 +765,6 @@ static void handle_filter(struct strbuf *line)
static void handle_body(void)
{
- int len = 0;
struct strbuf prev = STRBUF_INIT;
/* Skip up to the first boundary */
@@ -775,8 +774,6 @@ static void handle_body(void)
}
do {
- strbuf_setlen(&line, line.len + len);
-
/* process any boundary lines */
if (*content_top && is_multipart_boundary(&line)) {
/* flush any leftover */
@@ -832,10 +829,7 @@ static void handle_body(void)
handle_filter(&line);
}
- strbuf_reset(&line);
- if (strbuf_avail(&line) < 100)
- strbuf_grow(&line, 100);
- } while ((len = read_line_with_nul(line.buf, strbuf_avail(&line), fin)));
+ } while (!strbuf_getwholeline(&line, fin, '\n'));
handle_body_out:
strbuf_release(&prev);
diff --git a/builtin-mailsplit.c b/builtin-mailsplit.c
index ad5f6b593d..ee6ca0ebcd 100644
--- a/builtin-mailsplit.c
+++ b/builtin-mailsplit.c
@@ -7,6 +7,7 @@
#include "cache.h"
#include "builtin.h"
#include "string-list.h"
+#include "strbuf.h"
static const char git_mailsplit_usage[] =
"git mailsplit [-d<prec>] [-f<n>] [-b] -o<directory> [<mbox>|<Maildir>...]";
@@ -42,26 +43,8 @@ static int is_from_line(const char *line, int len)
return 1;
}
-/* Could be as small as 64, enough to hold a Unix "From " line. */
-static char buf[4096];
-
-/* We cannot use fgets() because our lines can contain NULs */
-int read_line_with_nul(char *buf, int size, FILE *in)
-{
- int len = 0, c;
-
- for (;;) {
- c = getc(in);
- if (c == EOF)
- break;
- buf[len++] = c;
- if (c == '\n' || len + 1 >= size)
- break;
- }
- buf[len] = '\0';
-
- return len;
-}
+static struct strbuf buf = STRBUF_INIT;
+static int keep_cr;
/* Called with the first line (potentially partial)
* already in buf[] -- normally that should begin with
@@ -71,10 +54,9 @@ int read_line_with_nul(char *buf, int size, FILE *in)
static int split_one(FILE *mbox, const char *name, int allow_bare)
{
FILE *output = NULL;
- int len = strlen(buf);
int fd;
int status = 0;
- int is_bare = !is_from_line(buf, len);
+ int is_bare = !is_from_line(buf.buf, buf.len);
if (is_bare && !allow_bare)
goto corrupt;
@@ -88,20 +70,23 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
* "From " and having something that looks like a date format.
*/
for (;;) {
- int is_partial = len && buf[len-1] != '\n';
+ if (!keep_cr && buf.len > 1 && buf.buf[buf.len-1] == '\n' &&
+ buf.buf[buf.len-2] == '\r') {
+ strbuf_setlen(&buf, buf.len-2);
+ strbuf_addch(&buf, '\n');
+ }
- if (fwrite(buf, 1, len, output) != len)
+ if (fwrite(buf.buf, 1, buf.len, output) != buf.len)
die_errno("cannot write output");
- len = read_line_with_nul(buf, sizeof(buf), mbox);
- if (len == 0) {
+ if (strbuf_getwholeline(&buf, mbox, '\n')) {
if (feof(mbox)) {
status = 1;
break;
}
die_errno("cannot read mbox");
}
- if (!is_partial && !is_bare && is_from_line(buf, len))
+ if (!is_bare && is_from_line(buf.buf, buf.len))
break; /* done with one message */
}
fclose(output);
@@ -166,7 +151,7 @@ static int split_maildir(const char *maildir, const char *dir,
goto out;
}
- if (fgets(buf, sizeof(buf), f) == NULL) {
+ if (strbuf_getwholeline(&buf, f, '\n')) {
error("cannot read mail %s (%s)", file, strerror(errno));
goto out;
}
@@ -203,7 +188,7 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
} while (isspace(peek));
ungetc(peek, f);
- if (fgets(buf, sizeof(buf), f) == NULL) {
+ if (strbuf_getwholeline(&buf, f, '\n')) {
/* empty stdin is OK */
if (f != stdin) {
error("cannot read mbox %s", file);
@@ -248,6 +233,8 @@ int cmd_mailsplit(int argc, const char **argv, const char *prefix)
nr = strtol(arg+2, NULL, 10);
} else if ( arg[1] == 'b' && !arg[2] ) {
allow_bare = 1;
+ } else if (!strcmp(arg, "--keep-cr")) {
+ keep_cr = 1;
} else if ( arg[1] == 'o' && arg[2] ) {
dir = arg+2;
} else if ( arg[1] == '-' && !arg[2] ) {
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index 9cc8a8451d..c4337480fd 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -2098,6 +2098,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
int rp_ac_alloc = 64;
int rp_ac;
+ read_replace_refs = 0;
+
rp_av = xcalloc(rp_ac_alloc, sizeof(*rp_av));
rp_av[0] = "pack-objects";
diff --git a/builtin-prune.c b/builtin-prune.c
index 0ed9cce4a2..8459aec8e8 100644
--- a/builtin-prune.c
+++ b/builtin-prune.c
@@ -140,6 +140,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
char *s;
save_commit_buffer = 0;
+ read_replace_refs = 0;
init_revisions(&revs, prefix);
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 9c2d634d6d..14c836b169 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -113,13 +113,15 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
argc = parse_options(argc, argv, unused_prefix, read_tree_options,
read_tree_usage, 0);
- if (read_cache_unmerged() && (opts.prefix || opts.merge))
- die("You need to resolve your current index first");
-
prefix_set = opts.prefix ? 1 : 0;
if (1 < opts.merge + opts.reset + prefix_set)
die("Which one? -m, --reset, or --prefix?");
- stage = opts.merge = (opts.reset || opts.merge || prefix_set);
+
+ if (opts.reset || opts.merge || opts.prefix) {
+ if (read_cache_unmerged() && (opts.prefix || opts.merge))
+ die("You need to resolve your current index first");
+ stage = opts.merge = 1;
+ }
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
diff --git a/builtin-replace.c b/builtin-replace.c
new file mode 100644
index 0000000000..fe3a647a36
--- /dev/null
+++ b/builtin-replace.c
@@ -0,0 +1,159 @@
+/*
+ * Builtin "git replace"
+ *
+ * Copyright (c) 2008 Christian Couder <chriscool@tuxfamily.org>
+ *
+ * Based on builtin-tag.c by Kristian Høgsberg <krh@redhat.com>
+ * and Carlos Rica <jasampler@gmail.com> that was itself based on
+ * git-tag.sh and mktag.c by Linus Torvalds.
+ */
+
+#include "cache.h"
+#include "builtin.h"
+#include "refs.h"
+#include "parse-options.h"
+
+static const char * const git_replace_usage[] = {
+ "git replace [-f] <object> <replacement>",
+ "git replace -d <object>...",
+ "git replace -l [<pattern>]",
+ NULL
+};
+
+static int show_reference(const char *refname, const unsigned char *sha1,
+ int flag, void *cb_data)
+{
+ const char *pattern = cb_data;
+
+ if (!fnmatch(pattern, refname, 0))
+ printf("%s\n", refname);
+
+ return 0;
+}
+
+static int list_replace_refs(const char *pattern)
+{
+ if (pattern == NULL)
+ pattern = "*";
+
+ for_each_replace_ref(show_reference, (void *) pattern);
+
+ return 0;
+}
+
+typedef int (*each_replace_name_fn)(const char *name, const char *ref,
+ const unsigned char *sha1);
+
+static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
+{
+ const char **p;
+ char ref[PATH_MAX];
+ int had_error = 0;
+ unsigned char sha1[20];
+
+ for (p = argv; *p; p++) {
+ if (snprintf(ref, sizeof(ref), "refs/replace/%s", *p)
+ >= sizeof(ref)) {
+ error("replace ref name too long: %.*s...", 50, *p);
+ had_error = 1;
+ continue;
+ }
+ if (!resolve_ref(ref, sha1, 1, NULL)) {
+ error("replace ref '%s' not found.", *p);
+ had_error = 1;
+ continue;
+ }
+ if (fn(*p, ref, sha1))
+ had_error = 1;
+ }
+ return had_error;
+}
+
+static int delete_replace_ref(const char *name, const char *ref,
+ const unsigned char *sha1)
+{
+ if (delete_ref(ref, sha1, 0))
+ return 1;
+ printf("Deleted replace ref '%s'\n", name);
+ return 0;
+}
+
+static int replace_object(const char *object_ref, const char *replace_ref,
+ int force)
+{
+ unsigned char object[20], prev[20], repl[20];
+ char ref[PATH_MAX];
+ struct ref_lock *lock;
+
+ if (get_sha1(object_ref, object))
+ die("Failed to resolve '%s' as a valid ref.", object_ref);
+ if (get_sha1(replace_ref, repl))
+ die("Failed to resolve '%s' as a valid ref.", replace_ref);
+
+ if (snprintf(ref, sizeof(ref),
+ "refs/replace/%s",
+ sha1_to_hex(object)) > sizeof(ref) - 1)
+ die("replace ref name too long: %.*s...", 50, ref);
+ if (check_ref_format(ref))
+ die("'%s' is not a valid ref name.", ref);
+
+ if (!resolve_ref(ref, prev, 1, NULL))
+ hashclr(prev);
+ else if (!force)
+ die("replace ref '%s' already exists", ref);
+
+ lock = lock_any_ref_for_update(ref, prev, 0);
+ if (!lock)
+ die("%s: cannot lock the ref", ref);
+ if (write_ref_sha1(lock, repl, NULL) < 0)
+ die("%s: cannot update the ref", ref);
+
+ return 0;
+}
+
+int cmd_replace(int argc, const char **argv, const char *prefix)
+{
+ int list = 0, delete = 0, force = 0;
+ struct option options[] = {
+ OPT_BOOLEAN('l', NULL, &list, "list replace refs"),
+ OPT_BOOLEAN('d', NULL, &delete, "delete replace refs"),
+ OPT_BOOLEAN('f', NULL, &force, "replace the ref if it exists"),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, options, git_replace_usage, 0);
+
+ if (list && delete)
+ usage_msg_opt("-l and -d cannot be used together",
+ git_replace_usage, options);
+
+ if (force && (list || delete))
+ usage_msg_opt("-f cannot be used with -d or -l",
+ git_replace_usage, options);
+
+ /* Delete refs */
+ if (delete) {
+ if (argc < 1)
+ usage_msg_opt("-d needs at least one argument",
+ git_replace_usage, options);
+ return for_each_replace_name(argv, delete_replace_ref);
+ }
+
+ /* Replace object */
+ if (!list && argc) {
+ if (argc != 2)
+ usage_msg_opt("bad number of arguments",
+ git_replace_usage, options);
+ return replace_object(argv[0], argv[1], force);
+ }
+
+ /* List refs, even if "list" is not set */
+ if (argc > 1)
+ usage_msg_opt("only one pattern can be given with -l",
+ git_replace_usage, options);
+ if (force)
+ usage_msg_opt("-f needs some arguments",
+ git_replace_usage, options);
+
+ return list_replace_refs(argv[0]);
+}
diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c
index 557148a693..685566e0b5 100644
--- a/builtin-unpack-objects.c
+++ b/builtin-unpack-objects.c
@@ -181,10 +181,10 @@ static void write_cached_object(struct object *obj)
static int check_object(struct object *obj, int type, void *data)
{
if (!obj)
- return 0;
+ return 1;
if (obj->flags & FLAG_WRITTEN)
- return 1;
+ return 0;
if (type != OBJ_ANY && obj->type != type)
die("object type mismatch");
@@ -195,22 +195,24 @@ static int check_object(struct object *obj, int type, void *data)
if (type != obj->type || type <= 0)
die("object of unexpected type");
obj->flags |= FLAG_WRITTEN;
- return 1;
+ return 0;
}
if (fsck_object(obj, 1, fsck_error_function))
die("Error in object");
- if (!fsck_walk(obj, check_object, NULL))
+ if (fsck_walk(obj, check_object, NULL))
die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
write_cached_object(obj);
- return 1;
+ return 0;
}
static void write_rest(void)
{
unsigned i;
- for (i = 0; i < nr_objects; i++)
- check_object(obj_list[i].obj, OBJ_ANY, NULL);
+ for (i = 0; i < nr_objects; i++) {
+ if (obj_list[i].obj)
+ check_object(obj_list[i].obj, OBJ_ANY, NULL);
+ }
}
static void added_object(unsigned nr, enum object_type type,
@@ -495,6 +497,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
int i;
unsigned char sha1[20];
+ read_replace_refs = 0;
+
git_config(git_default_config, NULL);
quiet = !isatty(2);
diff --git a/builtin-verify-pack.c b/builtin-verify-pack.c
index b5bd28e959..b6079ae6cb 100644
--- a/builtin-verify-pack.c
+++ b/builtin-verify-pack.c
@@ -6,10 +6,14 @@
#define MAX_CHAIN 50
-static void show_pack_info(struct packed_git *p)
+#define VERIFY_PACK_VERBOSE 01
+#define VERIFY_PACK_STAT_ONLY 02
+
+static void show_pack_info(struct packed_git *p, unsigned int flags)
{
uint32_t nr_objects, i;
int cnt;
+ int stat_only = flags & VERIFY_PACK_STAT_ONLY;
unsigned long chain_histogram[MAX_CHAIN+1], baseobjects;
nr_objects = p->num_objects;
@@ -32,16 +36,19 @@ static void show_pack_info(struct packed_git *p)
type = packed_object_info_detail(p, offset, &size, &store_size,
&delta_chain_length,
base_sha1);
- printf("%s ", sha1_to_hex(sha1));
+ if (!stat_only)
+ printf("%s ", sha1_to_hex(sha1));
if (!delta_chain_length) {
- printf("%-6s %lu %lu %"PRIuMAX"\n",
- type, size, store_size, (uintmax_t)offset);
+ if (!stat_only)
+ printf("%-6s %lu %lu %"PRIuMAX"\n",
+ type, size, store_size, (uintmax_t)offset);
baseobjects++;
}
else {
- printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
- type, size, store_size, (uintmax_t)offset,
- delta_chain_length, sha1_to_hex(base_sha1));
+ if (!stat_only)
+ printf("%-6s %lu %lu %"PRIuMAX" %u %s\n",
+ type, size, store_size, (uintmax_t)offset,
+ delta_chain_length, sha1_to_hex(base_sha1));
if (delta_chain_length <= MAX_CHAIN)
chain_histogram[delta_chain_length]++;
else
@@ -66,10 +73,12 @@ static void show_pack_info(struct packed_git *p)
chain_histogram[0] > 1 ? "s" : "");
}
-static int verify_one_pack(const char *path, int verbose)
+static int verify_one_pack(const char *path, unsigned int flags)
{
char arg[PATH_MAX];
int len;
+ int verbose = flags & VERIFY_PACK_VERBOSE;
+ int stat_only = flags & VERIFY_PACK_STAT_ONLY;
struct packed_git *pack;
int err;
@@ -105,14 +114,19 @@ static int verify_one_pack(const char *path, int verbose)
return error("packfile %s not found.", arg);
install_packed_git(pack);
- err = verify_pack(pack);
- if (verbose) {
+ if (!stat_only)
+ err = verify_pack(pack);
+ else
+ err = open_pack_index(pack);
+
+ if (verbose || stat_only) {
if (err)
printf("%s: bad\n", pack->pack_name);
else {
- show_pack_info(pack);
- printf("%s: ok\n", pack->pack_name);
+ show_pack_info(pack, flags);
+ if (!stat_only)
+ printf("%s: ok\n", pack->pack_name);
}
}
@@ -120,17 +134,20 @@ static int verify_one_pack(const char *path, int verbose)
}
static const char * const verify_pack_usage[] = {
- "git verify-pack [-v|--verbose] <pack>...",
+ "git verify-pack [-v|--verbose] [-s|--stat-only] <pack>...",
NULL
};
int cmd_verify_pack(int argc, const char **argv, const char *prefix)
{
int err = 0;
- int verbose = 0;
+ unsigned int flags = 0;
int i;
const struct option verify_pack_options[] = {
- OPT__VERBOSE(&verbose),
+ OPT_BIT('v', "verbose", &flags, "verbose",
+ VERIFY_PACK_VERBOSE),
+ OPT_BIT('s', "stat-only", &flags, "show statistics only",
+ VERIFY_PACK_STAT_ONLY),
OPT_END()
};
@@ -140,7 +157,7 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix)
if (argc < 1)
usage_with_options(verify_pack_usage, verify_pack_options);
for (i = 0; i < argc; i++) {
- if (verify_one_pack(argv[i], verbose))
+ if (verify_one_pack(argv[i], flags))
err = 1;
discard_revindex();
}
diff --git a/builtin.h b/builtin.h
index 20427d2963..51e4ba7f36 100644
--- a/builtin.h
+++ b/builtin.h
@@ -13,7 +13,6 @@ extern const char git_more_info_string[];
extern void list_common_cmds_help(void);
extern const char *help_unknown_cmd(const char *cmd);
extern void prune_packed_objects(int);
-extern int read_line_with_nul(char *buf, int size, FILE *file);
extern int fmt_merge_msg(int merge_summary, struct strbuf *in,
struct strbuf *out);
extern int commit_tree(const char *msg, unsigned char *tree,
@@ -112,5 +111,6 @@ extern int cmd_write_tree(int argc, const char **argv, const char *prefix);
extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);
extern int cmd_show_ref(int argc, const char **argv, const char *prefix);
extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);
+extern int cmd_replace(int argc, const char **argv, const char *prefix);
#endif
diff --git a/cache.h b/cache.h
index 9222774e6c..dd7f71eba5 100644
--- a/cache.h
+++ b/cache.h
@@ -469,7 +469,7 @@ extern int index_path(unsigned char *sha1, const char *path, struct stat *st, in
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
/* "careful lstat()" */
-extern int check_path(const char *path, int len, struct stat *st);
+extern int check_path(const char *path, int len, struct stat *st, int skiplen);
#define REFRESH_REALLY 0x0001 /* ignore_valid */
#define REFRESH_UNMERGED 0x0002 /* allow unmerged */
@@ -512,6 +512,7 @@ extern int log_all_ref_updates;
extern int warn_ambiguous_refs;
extern int shared_repository;
extern const char *apply_default_whitespace;
+extern const char *apply_default_ignorewhitespace;
extern int zlib_compression_level;
extern int core_compression_level;
extern int core_compression_seen;
@@ -519,6 +520,7 @@ extern size_t packed_git_window_size;
extern size_t packed_git_limit;
extern size_t delta_base_cache_limit;
extern int auto_crlf;
+extern int read_replace_refs;
extern int fsync_object_files;
extern int core_preload_index;
@@ -655,7 +657,11 @@ char *strip_path_suffix(const char *path, const char *suffix);
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
extern int sha1_object_info(const unsigned char *, unsigned long *);
-extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size);
+extern void *read_sha1_file_repl(const unsigned char *sha1, enum object_type *type, unsigned long *size, const unsigned char **replacement);
+static inline void *read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
+{
+ return read_sha1_file_repl(sha1, type, size, NULL);
+}
extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
diff --git a/commit.c b/commit.c
index e2bcbe8149..a6c6f70a92 100644
--- a/commit.c
+++ b/commit.c
@@ -564,13 +564,13 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
while (interesting(list)) {
struct commit *commit;
struct commit_list *parents;
- struct commit_list *n;
+ struct commit_list *next;
int flags;
commit = list->item;
- n = list->next;
+ next = list->next;
free(list);
- list = n;
+ list = next;
flags = commit->object.flags & (PARENT1 | PARENT2 | STALE);
if (flags == (PARENT1 | PARENT2)) {
@@ -598,11 +598,11 @@ static struct commit_list *merge_bases_many(struct commit *one, int n, struct co
free_commit_list(list);
list = result; result = NULL;
while (list) {
- struct commit_list *n = list->next;
+ struct commit_list *next = list->next;
if (!(list->item->object.flags & STALE))
insert_by_date(list->item, &result);
free(list);
- list = n;
+ list = next;
}
return result;
}
diff --git a/commit.h b/commit.h
index 4886544b63..d1674d7f16 100644
--- a/commit.h
+++ b/commit.h
@@ -123,6 +123,8 @@ struct commit_graft *read_graft_line(char *buf, int len);
int register_commit_graft(struct commit_graft *, int);
struct commit_graft *lookup_commit_graft(const unsigned char *sha1);
+const unsigned char *lookup_replace_object(const unsigned char *sha1);
+
extern struct commit_list *get_merge_bases(struct commit *rev1, struct commit *rev2, int cleanup);
extern struct commit_list *get_merge_bases_many(struct commit *one, int n, struct commit **twos, int cleanup);
extern struct commit_list *get_octopus_merge_bases(struct commit_list *in);
diff --git a/compat/snprintf.c b/compat/snprintf.c
index 6c0fb056a5..4d07087abd 100644
--- a/compat/snprintf.c
+++ b/compat/snprintf.c
@@ -3,7 +3,8 @@
/*
* The size parameter specifies the available space, i.e. includes
* the trailing NUL byte; but Windows's vsnprintf expects the
- * number of characters to write without the trailing NUL.
+ * number of characters to write, and does not necessarily write the
+ * trailing NUL.
*/
#ifndef SNPRINTF_SIZE_CORR
#if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ < 4
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 5543dc4d14..bf688e12e6 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -674,6 +674,7 @@ _git_am ()
--*)
__gitcomp "
--3way --committer-date-is-author-date --ignore-date
+ --ignore-whitespace --ignore-space-change
--interactive --keep --no-utf8 --signoff --utf8
--whitespace=
"
@@ -695,6 +696,7 @@ _git_apply ()
--stat --numstat --summary --check --index
--cached --index-info --reverse --reject --unidiff-zero
--apply --no-add --exclude=
+ --ignore-whitespace --ignore-space-change
--whitespace= --inaccurate-eof --verbose
"
return
@@ -1537,6 +1539,7 @@ _git_config ()
__gitcomp "
add.ignore-errors
alias.
+ apply.ignorewhitespace
apply.whitespace
branch.autosetupmerge
branch.autosetuprebase
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 342529db30..38438f3c4a 100755
--- a/contrib/fast-import/git-p4
+++ b/contrib/fast-import/git-p4
@@ -201,7 +201,7 @@ def isModeExec(mode):
def isModeExecChanged(src_mode, dst_mode):
return isModeExec(src_mode) != isModeExec(dst_mode)
-def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
+def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None):
cmd = p4_build_cmd("-G %s" % (cmd))
if verbose:
sys.stderr.write("Opening pipe: %s\n" % cmd)
@@ -224,7 +224,10 @@ def p4CmdList(cmd, stdin=None, stdin_mode='w+b'):
try:
while True:
entry = marshal.load(p4.stdout)
- result.append(entry)
+ if cb is not None:
+ cb(entry)
+ else:
+ result.append(entry)
except EOFError:
pass
exitCode = p4.wait()
@@ -950,10 +953,84 @@ class P4Sync(Command):
return branches
- ## Should move this out, doesn't use SELF.
- def readP4Files(self, files):
+ # output one file from the P4 stream
+ # - helper for streamP4Files
+
+ def streamOneP4File(self, file, contents):
+ if file["type"] == "apple":
+ print "\nfile %s is a strange apple file that forks. Ignoring" % \
+ file['depotFile']
+ return
+
+ relPath = self.stripRepoPath(file['depotFile'], self.branchPrefixes)
+ if verbose:
+ sys.stderr.write("%s\n" % relPath)
+
+ mode = "644"
+ if isP4Exec(file["type"]):
+ mode = "755"
+ elif file["type"] == "symlink":
+ mode = "120000"
+ # p4 print on a symlink contains "target\n", so strip it off
+ last = contents.pop()
+ last = last[:-1]
+ contents.append(last)
+
+ if self.isWindows and file["type"].endswith("text"):
+ mangled = []
+ for data in contents:
+ data = data.replace("\r\n", "\n")
+ mangled.append(data)
+ contents = mangled
+
+ if file['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
+ contents = map(lambda text: re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text), contents)
+ elif file['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
+ contents = map(lambda text: re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text), contents)
+
+ self.gitStream.write("M %s inline %s\n" % (mode, relPath))
+
+ # total length...
+ length = 0
+ for d in contents:
+ length = length + len(d)
+
+ self.gitStream.write("data %d\n" % length)
+ for d in contents:
+ self.gitStream.write(d)
+ self.gitStream.write("\n")
+
+ def streamOneP4Deletion(self, file):
+ relPath = self.stripRepoPath(file['path'], self.branchPrefixes)
+ if verbose:
+ sys.stderr.write("delete %s\n" % relPath)
+ self.gitStream.write("D %s\n" % relPath)
+
+ # handle another chunk of streaming data
+ def streamP4FilesCb(self, marshalled):
+
+ if marshalled.has_key('depotFile') and self.stream_have_file_info:
+ # start of a new file - output the old one first
+ self.streamOneP4File(self.stream_file, self.stream_contents)
+ self.stream_file = {}
+ self.stream_contents = []
+ self.stream_have_file_info = False
+
+ # pick up the new file information... for the
+ # 'data' field we need to append to our array
+ for k in marshalled.keys():
+ if k == 'data':
+ self.stream_contents.append(marshalled['data'])
+ else:
+ self.stream_file[k] = marshalled[k]
+
+ self.stream_have_file_info = True
+
+ # Stream directly from "p4 files" into "git fast-import"
+ def streamP4Files(self, files):
filesForCommit = []
filesToRead = []
+ filesToDelete = []
for f in files:
includeFile = True
@@ -967,50 +1044,35 @@ class P4Sync(Command):
filesForCommit.append(f)
if f['action'] not in ('delete', 'purge'):
filesToRead.append(f)
+ else:
+ filesToDelete.append(f)
- filedata = []
- if len(filesToRead) > 0:
- filedata = p4CmdList('-x - print',
- stdin='\n'.join(['%s#%s' % (f['path'], f['rev'])
- for f in filesToRead]),
- stdin_mode='w+')
-
- if "p4ExitCode" in filedata[0]:
- die("Problems executing p4. Error: [%d]."
- % (filedata[0]['p4ExitCode']));
-
- j = 0;
- contents = {}
- while j < len(filedata):
- stat = filedata[j]
- j += 1
- text = ''
- while j < len(filedata) and filedata[j]['code'] in ('text', 'unicode', 'binary'):
- text += filedata[j]['data']
- del filedata[j]['data']
- j += 1
-
- if not stat.has_key('depotFile'):
- sys.stderr.write("p4 print fails with: %s\n" % repr(stat))
- continue
+ # deleted files...
+ for f in filesToDelete:
+ self.streamOneP4Deletion(f)
- if stat['type'] in ('text+ko', 'unicode+ko', 'binary+ko'):
- text = re.sub(r'(?i)\$(Id|Header):[^$]*\$',r'$\1$', text)
- elif stat['type'] in ('text+k', 'ktext', 'kxtext', 'unicode+k', 'binary+k'):
- text = re.sub(r'\$(Id|Header|Author|Date|DateTime|Change|File|Revision):[^$\n]*\$',r'$\1$', text)
+ if len(filesToRead) > 0:
+ self.stream_file = {}
+ self.stream_contents = []
+ self.stream_have_file_info = False
- contents[stat['depotFile']] = text
+ # curry self argument
+ def streamP4FilesCbSelf(entry):
+ self.streamP4FilesCb(entry)
- for f in filesForCommit:
- path = f['path']
- if contents.has_key(path):
- f['data'] = contents[path]
+ p4CmdList("-x - print",
+ '\n'.join(['%s#%s' % (f['path'], f['rev'])
+ for f in filesToRead]),
+ cb=streamP4FilesCbSelf)
- return filesForCommit
+ # do the last chunk
+ if self.stream_file.has_key('depotFile'):
+ self.streamOneP4File(self.stream_file, self.stream_contents)
def commit(self, details, files, branch, branchPrefixes, parent = ""):
epoch = details["time"]
author = details["user"]
+ self.branchPrefixes = branchPrefixes
if self.verbose:
print "commit into %s" % branch
@@ -1023,7 +1085,6 @@ class P4Sync(Command):
new_files.append (f)
else:
sys.stderr.write("Ignoring file outside of prefix: %s\n" % path)
- files = self.readP4Files(new_files)
self.gitStream.write("commit %s\n" % branch)
# gitStream.write("mark :%s\n" % details["change"])
@@ -1051,33 +1112,7 @@ class P4Sync(Command):
print "parent %s" % parent
self.gitStream.write("from %s\n" % parent)
- for file in files:
- if file["type"] == "apple":
- print "\nfile %s is a strange apple file that forks. Ignoring!" % file['path']
- continue
-
- relPath = self.stripRepoPath(file['path'], branchPrefixes)
- if file["action"] in ("delete", "purge"):
- self.gitStream.write("D %s\n" % relPath)
- else:
- data = file['data']
-
- mode = "644"
- if isP4Exec(file["type"]):
- mode = "755"
- elif file["type"] == "symlink":
- mode = "120000"
- # p4 print on a symlink contains "target\n", so strip it off
- data = data[:-1]
-
- if self.isWindows and file["type"].endswith("text"):
- data = data.replace("\r\n", "\n")
-
- self.gitStream.write("M %s inline %s\n" % (mode, relPath))
- self.gitStream.write("data %s\n" % len(data))
- self.gitStream.write(data)
- self.gitStream.write("\n")
-
+ self.streamP4Files(new_files)
self.gitStream.write("\n")
change = int(details["change"])
diff --git a/diff-lib.c b/diff-lib.c
index ad2a4cde74..22da66ef14 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -162,7 +162,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
if (ce_uptodate(ce))
continue;
- changed = check_removed(ce, &st);
+ /* If CE_VALID is set, don't look at workdir for file removal */
+ changed = (ce->ce_flags & CE_VALID) ? 0 : check_removed(ce, &st);
if (changed) {
if (changed < 0) {
perror(ce->name);
@@ -337,6 +338,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,
struct rev_info *revs = o->unpack_data;
int match_missing, cached;
+ /* if the entry is not checked out, don't examine work tree */
+ cached = o->index_only || (idx && (idx->ce_flags & CE_VALID));
/*
* Backward compatibility wart - "diff-index -m" does
* not mean "do not ignore merges", but "match_missing".
@@ -344,7 +347,6 @@ static void do_oneway_diff(struct unpack_trees_options *o,
* But with the revision flag parsing, that's found in
* "!revs->ignore_merges".
*/
- cached = o->index_only;
match_missing = !revs->ignore_merges;
if (cached && idx && ce_stage(idx)) {
diff --git a/entry.c b/entry.c
index f276cf3b88..06d24f14c6 100644
--- a/entry.c
+++ b/entry.c
@@ -177,11 +177,15 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
/*
* This is like 'lstat()', except it refuses to follow symlinks
- * in the path.
+ * in the path, after skipping "skiplen".
*/
-int check_path(const char *path, int len, struct stat *st)
+int check_path(const char *path, int len, struct stat *st, int skiplen)
{
- if (has_symlink_leading_path(path, len)) {
+ const char *slash = path + len;
+
+ while (path < slash && *slash != '/')
+ slash--;
+ if (!has_dirs_only_path(path, slash - path, skiplen)) {
errno = ENOENT;
return -1;
}
@@ -201,7 +205,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
strcpy(path + len, ce->name);
len += ce_namelen(ce);
- if (!check_path(path, len, &st)) {
+ if (!check_path(path, len, &st, state->base_dir_len)) {
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
if (!changed)
return 0;
diff --git a/environment.c b/environment.c
index 8f5eaa7dd8..5de6837840 100644
--- a/environment.c
+++ b/environment.c
@@ -26,6 +26,7 @@ const char *git_commit_encoding;
const char *git_log_output_encoding;
int shared_repository = PERM_UMASK;
const char *apply_default_whitespace;
+const char *apply_default_ignorewhitespace;
int zlib_compression_level = Z_BEST_SPEED;
int core_compression_level;
int core_compression_seen;
@@ -38,6 +39,7 @@ int pager_use_color = 1;
const char *editor_program;
const char *excludes_file;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
+int read_replace_refs = 1;
enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index df9f231635..06f70602cc 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -841,6 +841,10 @@ sub coalesce_overlapping_hunks {
my ($last_o_ctx, $last_was_dirty);
for (grep { $_->{USE} } @in) {
+ if ($_->{TYPE} ne 'hunk') {
+ push @out, $_;
+ next;
+ }
my $text = $_->{TEXT};
my ($o_ofs) = parse_hunk_header($text->[0]);
if (defined $last_o_ctx &&
diff --git a/git-am.sh b/git-am.sh
index f719f6e654..3c03f3e0df 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -16,6 +16,8 @@ s,signoff add a Signed-off-by line to the commit message
u,utf8 recode into utf8 (default)
k,keep pass -k flag to git-mailinfo
whitespace= pass it through git-apply
+ignore-space-change pass it through git-apply
+ignore-whitespace pass it through git-apply
directory= pass it through git-apply
C= pass it through git-apply
p= pass it through git-apply
@@ -211,7 +213,13 @@ check_patch_format () {
split_patches () {
case "$patch_format" in
mbox)
- git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||
+ case "$rebasing" in
+ '')
+ keep_cr= ;;
+ ?*)
+ keep_cr=--keep-cr ;;
+ esac
+ git mailsplit -d"$prec" -o"$dotest" -b $keep_cr -- "$@" > "$dotest/last" ||
clean_abort
;;
stgit-series)
@@ -321,7 +329,7 @@ do
git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
--patch-format)
shift ; patch_format="$1" ;;
- --reject)
+ --reject|--ignore-whitespace|--ignore-space-change)
git_apply_opt="$git_apply_opt $1" ;;
--committer-date-is-author-date)
committer_date_is_author_date=t ;;
diff --git a/git-bisect.sh b/git-bisect.sh
index 8969553658..6f6f03966f 100755
--- a/git-bisect.sh
+++ b/git-bisect.sh
@@ -405,7 +405,7 @@ bisect_run () {
exit $res
fi
- if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
+ if grep "is the first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
echo "bisect run success"
exit 0;
fi
diff --git a/git-compat-util.h b/git-compat-util.h
index 9f941e42b1..71b5acb6fa 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -57,10 +57,8 @@
# endif
#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__USLC__) && !defined(_M_UNIX) && !defined(sgi)
#define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
-#ifndef __sun__
#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
#endif
-#endif
#define _ALL_SOURCE 1
#define _GNU_SOURCE 1
#define _BSD_SOURCE 1
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index 37e044db40..a480d6fc70 100755
--- a/git-filter-branch.sh
+++ b/git-filter-branch.sh
@@ -97,12 +97,12 @@ set_ident () {
echo "case \"\$GIT_${uid}_NAME\" in \"\") GIT_${uid}_NAME=\"\${GIT_${uid}_EMAIL%%@*}\" && export GIT_${uid}_NAME;; esac"
}
-USAGE="[--env-filter <command>] [--tree-filter <command>] \
-[--index-filter <command>] [--parent-filter <command>] \
-[--msg-filter <command>] [--commit-filter <command>] \
-[--tag-name-filter <command>] [--subdirectory-filter <directory>] \
-[--original <namespace>] [-d <directory>] [-f | --force] \
-[<rev-list options>...]"
+USAGE="[--env-filter <command>] [--tree-filter <command>]
+ [--index-filter <command>] [--parent-filter <command>]
+ [--msg-filter <command>] [--commit-filter <command>]
+ [--tag-name-filter <command>] [--subdirectory-filter <directory>]
+ [--original <namespace>] [-d <directory>] [-f | --force]
+ [<rev-list options>...]"
OPTIONS_SPEC=
. git-sh-setup
diff --git a/git-instaweb.sh b/git-instaweb.sh
index 5f5cac75ea..d96eddbe56 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -77,11 +77,30 @@ start_httpd () {
resolve_full_httpd
# don't quote $full_httpd, there can be arguments to it (-f)
- $full_httpd "$fqgitdir/gitweb/httpd.conf"
- if test $? != 0; then
- echo "Could not execute http daemon $httpd."
- exit 1
- fi
+ case "$httpd" in
+ *mongoose*)
+ #The mongoose server doesn't have a daemon mode so we'll have to fork it
+ $full_httpd "$fqgitdir/gitweb/httpd.conf" &
+ #Save the pid before doing anything else (we'll print it later)
+ pid=$!
+
+ if test $? != 0; then
+ echo "Could not execute http daemon $httpd."
+ exit 1
+ fi
+
+ cat > "$fqgitdir/pid" <<EOF
+$pid
+EOF
+ ;;
+ *)
+ $full_httpd "$fqgitdir/gitweb/httpd.conf"
+ if test $? != 0; then
+ echo "Could not execute http daemon $httpd."
+ exit 1
+ fi
+ ;;
+ esac
}
stop_httpd () {
@@ -308,6 +327,31 @@ EOF
fi
}
+mongoose_conf() {
+ cat > "$conf" <<EOF
+# Mongoose web server configuration file.
+# Lines starting with '#' and empty lines are ignored.
+# For detailed description of every option, visit
+# http://code.google.com/p/mongoose/wiki/MongooseManual
+
+root $fqgitdir/gitweb
+ports $port
+index_files gitweb.cgi
+#ssl_cert $fqgitdir/gitweb/ssl_cert.pem
+error_log $fqgitdir/gitweb/error.log
+access_log $fqgitdir/gitweb/access.log
+
+#cgi setup
+cgi_env PATH=/usr/local/bin:/usr/bin:/bin,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH
+cgi_interp $PERL
+cgi_ext cgi,pl
+
+# mimetype mapping
+mime_types .gz=application/x-gzip,.tar.gz=application/x-tgz,.tgz=application/x-tgz,.tar=application/x-tar,.zip=application/zip,.gif=image/gif,.jpg=image/jpeg,.jpeg=image/jpeg,.png=image/png,.css=text/css,.html=text/html,.htm=text/html,.js=text/javascript,.c=text/plain,.cpp=text/plain,.log=text/plain,.conf=text/plain,.text=text/plain,.txt=text/plain,.dtd=text/xml,.bz2=application/x-bzip,.tbz=application/x-bzip-compressed-tar,.tar.bz2=application/x-bzip-compressed-tar
+EOF
+}
+
+
script='
s#^(my|our) \$projectroot =.*#$1 \$projectroot = "'$(dirname "$fqgitdir")'";#;
s#(my|our) \$gitbin =.*#$1 \$gitbin = "'$GIT_EXEC_PATH'";#;
@@ -344,6 +388,9 @@ case "$httpd" in
webrick)
webrick_conf
;;
+*mongoose*)
+ mongoose_conf
+ ;;
*)
echo "Unknown httpd specified: $httpd"
exit 1
diff --git a/git-rebase.sh b/git-rebase.sh
index 3555d17a5d..2315d95a9f 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -333,6 +333,9 @@ do
;;
esac
;;
+ --ignore-whitespace)
+ git_am_opt="$git_am_opt $1"
+ ;;
--committer-date-is-author-date|--ignore-date)
git_am_opt="$git_am_opt $1"
force_rebase=t
diff --git a/git-stash.sh b/git-stash.sh
index 03e589f764..d61c9d03bc 100755
--- a/git-stash.sh
+++ b/git-stash.sh
@@ -162,10 +162,6 @@ show_stash () {
}
apply_stash () {
- git update-index -q --refresh &&
- git diff-files --quiet --ignore-submodules ||
- die 'Cannot apply to a dirty working tree, please stage your changes'
-
unstash_index=
while test $# != 0
@@ -184,18 +180,27 @@ apply_stash () {
shift
done
- # current index state
- c_tree=$(git write-tree) ||
- die 'Cannot apply a stash in the middle of a merge'
+ if test $# = 0
+ then
+ have_stash || die 'Nothing to apply'
+ fi
# stash records the work tree, and is a merge between the
# base commit (first parent) and the index tree (second parent).
- s=$(git rev-parse --verify --default $ref_stash "$@") &&
- w_tree=$(git rev-parse --verify "$s:") &&
- b_tree=$(git rev-parse --verify "$s^1:") &&
- i_tree=$(git rev-parse --verify "$s^2:") ||
+ s=$(git rev-parse --quiet --verify --default $ref_stash "$@") &&
+ w_tree=$(git rev-parse --quiet --verify "$s:") &&
+ b_tree=$(git rev-parse --quiet --verify "$s^1:") &&
+ i_tree=$(git rev-parse --quiet --verify "$s^2:") ||
die "$*: no valid stashed state found"
+ git update-index -q --refresh &&
+ git diff-files --quiet --ignore-submodules ||
+ die 'Cannot apply to a dirty working tree, please stage your changes'
+
+ # current index state
+ c_tree=$(git write-tree) ||
+ die 'Cannot apply a stash in the middle of a merge'
+
unstashed_index_tree=
if test -n "$unstash_index" && test "$b_tree" != "$i_tree" &&
test "$c_tree" != "$i_tree"
diff --git a/git-submodule.sh b/git-submodule.sh
index 446bbc0a10..bfbd36b6f4 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -9,7 +9,7 @@ USAGE="[--quiet] add [-b branch] [--reference <repository>] [--] <repository> <p
or: $dashless [--quiet] status [--cached] [--recursive] [--] [<path>...]
or: $dashless [--quiet] init [--] [<path>...]
or: $dashless [--quiet] update [--init] [-N|--no-fetch] [--rebase] [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
- or: $dashless [--quiet] summary [--cached] [--summary-limit <n>] [commit] [--] [<path>...]
+ or: $dashless [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
or: $dashless [--quiet] foreach [--recursive] <command>
or: $dashless [--quiet] sync [--] [<path>...]"
OPTIONS_SPEC=
@@ -21,6 +21,7 @@ command=
branch=
reference=
cached=
+files=
nofetch=
update=
prefix=
@@ -507,6 +508,7 @@ set_name_rev () {
cmd_summary() {
summary_limit=-1
for_status=
+ diff_cmd=diff-index
# parse $args after "submodule ... summary".
while test $# -ne 0
@@ -515,6 +517,9 @@ cmd_summary() {
--cached)
cached="$1"
;;
+ --files)
+ files="$1"
+ ;;
--for-status)
for_status="$1"
;;
@@ -551,9 +556,17 @@ cmd_summary() {
head=HEAD
fi
+ if [ -n "$files" ]
+ then
+ test -n "$cached" &&
+ die "--cached cannot be used with --files"
+ diff_cmd=diff-files
+ head=
+ fi
+
cd_to_toplevel
# Get modified modules cared by user
- modules=$(git diff-index $cached --raw $head -- "$@" |
+ modules=$(git $diff_cmd $cached --raw $head -- "$@" |
egrep '^:([0-7]* )?160000' |
while read mod_src mod_dst sha1_src sha1_dst status name
do
@@ -567,7 +580,7 @@ cmd_summary() {
test -z "$modules" && return
- git diff-index $cached --raw $head -- $modules |
+ git $diff_cmd $cached --raw $head -- $modules |
egrep '^:([0-7]* )?160000' |
cut -c2- |
while read mod_src mod_dst sha1_src sha1_dst status name
diff --git a/git-svn.perl b/git-svn.perl
index 1da9f0781c..ce4fef9d34 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -21,6 +21,15 @@ $Git::SVN::default_ref_id = $ENV{GIT_SVN_ID} || 'git-svn';
$Git::SVN::Ra::_log_window_size = 100;
$Git::SVN::_minimize_url = 'unset';
+if (! exists $ENV{SVN_SSH}) {
+ if (exists $ENV{GIT_SSH}) {
+ $ENV{SVN_SSH} = $ENV{GIT_SSH};
+ if ($^O eq 'msys') {
+ $ENV{SVN_SSH} =~ s/\\/\\\\/g;
+ }
+ }
+}
+
$Git::SVN::Log::TZ = $ENV{TZ};
$ENV{TZ} = 'UTC';
$| = 1; # unbuffer STDOUT
@@ -1222,6 +1231,7 @@ sub complete_url_ls_init {
}
command_oneline('config', $k, $gs->{url}) unless $orig_url;
my $remote_path = "$gs->{path}/$repo_path";
+ $remote_path =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
$remote_path =~ s#/+#/#g;
$remote_path =~ s#^/##g;
$remote_path .= "/*" if $remote_path !~ /\*/;
@@ -1892,6 +1902,7 @@ sub init_remote_config {
command_noisy('config',
"svn-remote.$self->{repo_id}.url", $url);
$self->{path} =~ s{^/}{};
+ $self->{path} =~ s{%([0-9A-F]{2})}{chr hex($1)}ieg;
command_noisy('config', '--add',
"svn-remote.$self->{repo_id}.fetch",
"$self->{path}:".$self->refname);
diff --git a/git.c b/git.c
index 4588a8bac2..5da6c65697 100644
--- a/git.c
+++ b/git.c
@@ -339,6 +339,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "receive-pack", cmd_receive_pack },
{ "reflog", cmd_reflog, RUN_SETUP },
{ "remote", cmd_remote, RUN_SETUP },
+ { "replace", cmd_replace, RUN_SETUP },
{ "repo-config", cmd_config },
{ "rerere", cmd_rerere, RUN_SETUP },
{ "reset", cmd_reset, RUN_SETUP },
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
index 18c9ce35e8..b76a0cffff 100644
--- a/gitweb/INSTALL
+++ b/gitweb/INSTALL
@@ -123,6 +123,15 @@ GITWEB_CONFIG file:
$feature{'snapshot'}{'default'} = ['zip', 'tgz'];
$feature{'snapshot'}{'override'} = 1;
+If you allow overriding for the snapshot feature, you can specify which
+snapshot formats are globally disabled. You can also add any command line
+options you want (such as setting the compression level). For instance,
+you can disable Zip compressed snapshots and set GZip to run at level 6 by
+adding the following lines to your $GITWEB_CONFIG:
+
+ $known_snapshot_formats{'zip'}{'disabled'} = 1;
+ $known_snapshot_formats{'tgz'}{'compressor'} = ['gzip','-6'];
+
Gitweb repositories
-------------------
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index be7358fdeb..d02b7a3c26 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -160,7 +160,8 @@ our %known_snapshot_formats = (
# 'suffix' => filename suffix,
# 'format' => --format for git-archive,
# 'compressor' => [compressor command and arguments]
- # (array reference, optional)}
+ # (array reference, optional)
+ # 'disabled' => boolean (optional)}
#
'tgz' => {
'display' => 'tar.gz',
@@ -176,6 +177,14 @@ our %known_snapshot_formats = (
'format' => 'tar',
'compressor' => ['bzip2']},
+ 'txz' => {
+ 'display' => 'tar.xz',
+ 'type' => 'application/x-xz',
+ 'suffix' => '.tar.xz',
+ 'format' => 'tar',
+ 'compressor' => ['xz'],
+ 'disabled' => 1},
+
'zip' => {
'display' => 'zip',
'type' => 'application/x-zip',
@@ -188,6 +197,7 @@ our %known_snapshot_formats = (
our %known_snapshot_format_aliases = (
'gzip' => 'tgz',
'bzip2' => 'tbz2',
+ 'xz' => 'txz',
# backward compatibility: legacy gitweb config support
'x-gzip' => undef, 'gz' => undef,
@@ -494,7 +504,8 @@ sub filter_snapshot_fmts {
exists $known_snapshot_format_aliases{$_} ?
$known_snapshot_format_aliases{$_} : $_} @fmts;
@fmts = grep {
- exists $known_snapshot_formats{$_} } @fmts;
+ exists $known_snapshot_formats{$_} &&
+ !$known_snapshot_formats{$_}{'disabled'}} @fmts;
}
our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
@@ -1513,10 +1524,10 @@ sub format_subject_html {
$long =~ s/[[:cntrl:]]/?/g;
return $cgi->a({-href => $href, -class => "list subject",
-title => to_utf8($long)},
- esc_html($short) . $extra);
+ esc_html($short)) . $extra;
} else {
return $cgi->a({-href => $href, -class => "list subject"},
- esc_html($long) . $extra);
+ esc_html($long)) . $extra;
}
}
@@ -5181,6 +5192,8 @@ sub git_snapshot {
die_error(400, "Unknown snapshot format");
} elsif (!grep($_ eq $format, @snapshot_fmts)) {
die_error(403, "Unsupported snapshot format");
+ } elsif ($known_snapshot_formats{$format}{'disabled'}) {
+ die_error(403, "Snapshot format not allowed");
}
if (!defined $hash) {
diff --git a/graph.c b/graph.c
index e466770208..f3226ec94a 100644
--- a/graph.c
+++ b/graph.c
@@ -225,7 +225,12 @@ struct git_graph *graph_init(struct rev_info *opt)
graph->num_columns = 0;
graph->num_new_columns = 0;
graph->mapping_size = 0;
- graph->default_column_color = 0;
+ /*
+ * Start the column color at the maximum value, since we'll
+ * always increment it for the first commit we output.
+ * This way we start at 0 for the first commit.
+ */
+ graph->default_column_color = COLUMN_COLORS_MAX - 1;
/*
* Allocate a reasonably large default number of columns
@@ -499,11 +504,14 @@ static void graph_update_columns(struct git_graph *graph)
parent;
parent = next_interesting_parent(graph, parent)) {
/*
- * If this is a merge increment the current
+ * If this is a merge, or the start of a new
+ * childless column, increment the current
* color.
*/
- if (graph->num_parents > 1)
+ if (graph->num_parents > 1 ||
+ !is_commit_in_columns) {
graph_increment_column_color(graph);
+ }
graph_insert_into_new_columns(graph,
parent->item,
&mapping_idx);
diff --git a/http.c b/http.c
index 14d535747d..eb0c669801 100644
--- a/http.c
+++ b/http.c
@@ -719,7 +719,9 @@ void append_remote_object_url(struct strbuf *buf, const char *url,
const char *hex,
int only_two_digit_prefix)
{
- strbuf_addf(buf, "%s/objects/%.*s/", url, 2, hex);
+ end_url_with_slash(buf, url);
+
+ strbuf_addf(buf, "objects/%.*s/", 2, hex);
if (!only_two_digit_prefix)
strbuf_addf(buf, "%s", hex+2);
}
diff --git a/log-tree.c b/log-tree.c
index a3b4c0692c..1c9eefee33 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -25,7 +25,8 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
struct object *obj = parse_object(sha1);
if (!obj)
return 0;
- refname = prettify_refname(refname);
+ if (!cb_data || *(int *)cb_data == DECORATE_SHORT_REFS)
+ refname = prettify_refname(refname);
add_name_decoration("", refname, obj);
while (obj->type == OBJ_TAG) {
obj = ((struct tag *)obj)->tagged;
@@ -36,12 +37,12 @@ static int add_ref_decoration(const char *refname, const unsigned char *sha1, in
return 0;
}
-void load_ref_decorations(void)
+void load_ref_decorations(int flags)
{
static int loaded;
if (!loaded) {
loaded = 1;
- for_each_ref(add_ref_decoration, NULL);
+ for_each_ref(add_ref_decoration, &flags);
}
}
diff --git a/log-tree.h b/log-tree.h
index 20b5caf1aa..3f7b40027b 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -17,7 +17,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
const char **subject_p,
const char **extra_headers_p,
int *need_8bit_cte_p);
-void load_ref_decorations(void);
+void load_ref_decorations(int flags);
#define FORMAT_PATCH_NAME_MAX 64
void get_patch_filename(struct commit *commit, int nr, const char *suffix,
diff --git a/mktag.c b/mktag.c
index a609e3ebd1..a3b4270c18 100644
--- a/mktag.c
+++ b/mktag.c
@@ -19,16 +19,17 @@
/*
* We refuse to tag something we can't verify. Just because.
*/
-static int verify_object(unsigned char *sha1, const char *expected_type)
+static int verify_object(const unsigned char *sha1, const char *expected_type)
{
int ret = -1;
enum object_type type;
unsigned long size;
- void *buffer = read_sha1_file(sha1, &type, &size);
+ const unsigned char *repl;
+ void *buffer = read_sha1_file_repl(sha1, &type, &size, &repl);
if (buffer) {
if (type == type_from_string(expected_type))
- ret = check_sha1_signature(sha1, buffer, size, expected_type);
+ ret = check_sha1_signature(repl, buffer, size, expected_type);
free(buffer);
}
return ret;
diff --git a/object.c b/object.c
index a6ef439192..fe8eaaf19f 100644
--- a/object.c
+++ b/object.c
@@ -188,17 +188,18 @@ struct object *parse_object(const unsigned char *sha1)
unsigned long size;
enum object_type type;
int eaten;
- void *buffer = read_sha1_file(sha1, &type, &size);
+ const unsigned char *repl;
+ void *buffer = read_sha1_file_repl(sha1, &type, &size, &repl);
if (buffer) {
struct object *obj;
- if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0) {
+ if (check_sha1_signature(repl, buffer, size, typename(type)) < 0) {
free(buffer);
- error("sha1 mismatch %s\n", sha1_to_hex(sha1));
+ error("sha1 mismatch %s\n", sha1_to_hex(repl));
return NULL;
}
- obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
+ obj = parse_object_buffer(repl, type, size, buffer, &eaten);
if (!eaten)
free(buffer);
return obj;
diff --git a/parse-options.c b/parse-options.c
index 3b71fbb541..a64a4d6ee2 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -549,6 +549,14 @@ void usage_with_options(const char * const *usagestr,
exit(129);
}
+void usage_msg_opt(const char *msg,
+ const char * const *usagestr,
+ const struct option *options)
+{
+ fprintf(stderr, "%s\n\n", msg);
+ usage_with_options(usagestr, options);
+}
+
int parse_options_usage(const char * const *usagestr,
const struct option *opts)
{
diff --git a/parse-options.h b/parse-options.h
index b32587ad7c..f295a2cf85 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -145,6 +145,10 @@ extern int parse_options(int argc, const char **argv, const char *prefix,
extern NORETURN void usage_with_options(const char * const *usagestr,
const struct option *options);
+extern NORETURN void usage_msg_opt(const char *msg,
+ const char * const *usagestr,
+ const struct option *options);
+
/*----- incremental advanced APIs -----*/
enum {
diff --git a/pretty.c b/pretty.c
index 3b2ecdd20e..f5983f8baa 100644
--- a/pretty.c
+++ b/pretty.c
@@ -583,7 +583,7 @@ static void format_decoration(struct strbuf *sb, const struct commit *commit)
struct name_decoration *d;
const char *prefix = " (";
- load_ref_decorations();
+ load_ref_decorations(DECORATE_SHORT_REFS);
d = lookup_decoration(&name_decoration, &commit->object);
while (d) {
strbuf_addstr(sb, prefix);
diff --git a/refs.c b/refs.c
index dd9c9ba3f6..24865cf5a6 100644
--- a/refs.c
+++ b/refs.c
@@ -668,6 +668,11 @@ int for_each_remote_ref(each_ref_fn fn, void *cb_data)
return for_each_ref_in("refs/remotes/", fn, cb_data);
}
+int for_each_replace_ref(each_ref_fn fn, void *cb_data)
+{
+ return do_for_each_ref("refs/replace/", fn, 13, 0, cb_data);
+}
+
int for_each_rawref(each_ref_fn fn, void *cb_data)
{
return do_for_each_ref("refs/", fn, 0,
diff --git a/refs.h b/refs.h
index c11f6a6d58..777b5b7ca6 100644
--- a/refs.h
+++ b/refs.h
@@ -24,6 +24,7 @@ extern int for_each_ref_in(const char *, each_ref_fn, void *);
extern int for_each_tag_ref(each_ref_fn, void *);
extern int for_each_branch_ref(each_ref_fn, void *);
extern int for_each_remote_ref(each_ref_fn, void *);
+extern int for_each_replace_ref(each_ref_fn, void *);
/* can be used to learn about broken ref and symref */
extern int for_each_rawref(each_ref_fn, void *);
diff --git a/replace_object.c b/replace_object.c
new file mode 100644
index 0000000000..eb59604fd3
--- /dev/null
+++ b/replace_object.c
@@ -0,0 +1,114 @@
+#include "cache.h"
+#include "sha1-lookup.h"
+#include "refs.h"
+
+static struct replace_object {
+ unsigned char sha1[2][20];
+} **replace_object;
+
+static int replace_object_alloc, replace_object_nr;
+
+static const unsigned char *replace_sha1_access(size_t index, void *table)
+{
+ struct replace_object **replace = table;
+ return replace[index]->sha1[0];
+}
+
+static int replace_object_pos(const unsigned char *sha1)
+{
+ return sha1_pos(sha1, replace_object, replace_object_nr,
+ replace_sha1_access);
+}
+
+static int register_replace_object(struct replace_object *replace,
+ int ignore_dups)
+{
+ int pos = replace_object_pos(replace->sha1[0]);
+
+ if (0 <= pos) {
+ if (ignore_dups)
+ free(replace);
+ else {
+ free(replace_object[pos]);
+ replace_object[pos] = replace;
+ }
+ return 1;
+ }
+ pos = -pos - 1;
+ if (replace_object_alloc <= ++replace_object_nr) {
+ replace_object_alloc = alloc_nr(replace_object_alloc);
+ replace_object = xrealloc(replace_object,
+ sizeof(*replace_object) *
+ replace_object_alloc);
+ }
+ if (pos < replace_object_nr)
+ memmove(replace_object + pos + 1,
+ replace_object + pos,
+ (replace_object_nr - pos - 1) *
+ sizeof(*replace_object));
+ replace_object[pos] = replace;
+ return 0;
+}
+
+static int register_replace_ref(const char *refname,
+ const unsigned char *sha1,
+ int flag, void *cb_data)
+{
+ /* Get sha1 from refname */
+ const char *slash = strrchr(refname, '/');
+ const char *hash = slash ? slash + 1 : refname;
+ struct replace_object *repl_obj = xmalloc(sizeof(*repl_obj));
+
+ if (strlen(hash) != 40 || get_sha1_hex(hash, repl_obj->sha1[0])) {
+ free(repl_obj);
+ warning("bad replace ref name: %s", refname);
+ return 0;
+ }
+
+ /* Copy sha1 from the read ref */
+ hashcpy(repl_obj->sha1[1], sha1);
+
+ /* Register new object */
+ if (register_replace_object(repl_obj, 1))
+ die("duplicate replace ref: %s", refname);
+
+ return 0;
+}
+
+static void prepare_replace_object(void)
+{
+ static int replace_object_prepared;
+
+ if (replace_object_prepared)
+ return;
+
+ for_each_replace_ref(register_replace_ref, NULL);
+ replace_object_prepared = 1;
+}
+
+/* We allow "recursive" replacement. Only within reason, though */
+#define MAXREPLACEDEPTH 5
+
+const unsigned char *lookup_replace_object(const unsigned char *sha1)
+{
+ int pos, depth = MAXREPLACEDEPTH;
+ const unsigned char *cur = sha1;
+
+ if (!read_replace_refs)
+ return sha1;
+
+ prepare_replace_object();
+
+ /* Try to recursively replace the object */
+ do {
+ if (--depth < 0)
+ die("replace depth too high for object %s",
+ sha1_to_hex(sha1));
+
+ pos = replace_object_pos(cur);
+ if (0 <= pos)
+ cur = replace_object[pos]->sha1[1];
+ } while (0 <= pos);
+
+ return cur;
+}
diff --git a/revision.c b/revision.c
index 9f5dac5f1d..ce24ad9a8d 100644
--- a/revision.c
+++ b/revision.c
@@ -1052,7 +1052,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->simplify_by_decoration = 1;
revs->limited = 1;
revs->prune = 1;
- load_ref_decorations();
+ load_ref_decorations(DECORATE_SHORT_REFS);
} else if (!strcmp(arg, "--date-order")) {
revs->lifo = 0;
revs->topo_order = 1;
diff --git a/revision.h b/revision.h
index fb74492714..b10984b603 100644
--- a/revision.h
+++ b/revision.h
@@ -15,6 +15,9 @@
#define SYMMETRIC_LEFT (1u<<8)
#define ALL_REV_FLAGS ((1u<<9)-1)
+#define DECORATE_SHORT_REFS 1
+#define DECORATE_FULL_REFS 2
+
struct rev_info;
struct log_info;
diff --git a/sha1_file.c b/sha1_file.c
index 1d996a1990..4ea0b18d0a 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -2144,13 +2144,26 @@ static void *read_object(const unsigned char *sha1, enum object_type *type,
return read_packed_sha1(sha1, type, size);
}
-void *read_sha1_file(const unsigned char *sha1, enum object_type *type,
- unsigned long *size)
+void *read_sha1_file_repl(const unsigned char *sha1,
+ enum object_type *type,
+ unsigned long *size,
+ const unsigned char **replacement)
{
- void *data = read_object(sha1, type, size);
+ const unsigned char *repl = lookup_replace_object(sha1);
+ void *data = read_object(repl, type, size);
+
+ /* die if we replaced an object with one that does not exist */
+ if (!data && repl != sha1)
+ die("replacement %s not found for %s",
+ sha1_to_hex(repl), sha1_to_hex(sha1));
+
/* legacy behavior is to die on corrupted objects */
- if (!data && (has_loose_object(sha1) || has_packed_and_bad(sha1)))
- die("object %s is corrupted", sha1_to_hex(sha1));
+ if (!data && (has_loose_object(repl) || has_packed_and_bad(repl)))
+ die("object %s is corrupted", sha1_to_hex(repl));
+
+ if (replacement)
+ *replacement = repl;
+
return data;
}
diff --git a/strbuf.c b/strbuf.c
index f03d11702b..a6153dca27 100644
--- a/strbuf.c
+++ b/strbuf.c
@@ -322,7 +322,7 @@ int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint)
return -1;
}
-int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
+int strbuf_getwholeline(struct strbuf *sb, FILE *fp, int term)
{
int ch;
@@ -332,10 +332,10 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
strbuf_reset(sb);
while ((ch = fgetc(fp)) != EOF) {
- if (ch == term)
- break;
strbuf_grow(sb, 1);
sb->buf[sb->len++] = ch;
+ if (ch == term)
+ break;
}
if (ch == EOF && sb->len == 0)
return EOF;
@@ -344,6 +344,15 @@ int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
return 0;
}
+int strbuf_getline(struct strbuf *sb, FILE *fp, int term)
+{
+ if (strbuf_getwholeline(sb, fp, term))
+ return EOF;
+ if (sb->buf[sb->len-1] == term)
+ strbuf_setlen(sb, sb->len-1);
+ return 0;
+}
+
int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint)
{
int fd, len;
diff --git a/strbuf.h b/strbuf.h
index eaa8704d5f..d05e056dd3 100644
--- a/strbuf.h
+++ b/strbuf.h
@@ -126,6 +126,7 @@ extern ssize_t strbuf_read(struct strbuf *, int fd, size_t hint);
extern int strbuf_read_file(struct strbuf *sb, const char *path, size_t hint);
extern int strbuf_readlink(struct strbuf *sb, const char *path, size_t hint);
+extern int strbuf_getwholeline(struct strbuf *, FILE *, int);
extern int strbuf_getline(struct strbuf *, FILE *, int);
extern void stripspace(struct strbuf *buf, int skip_comments);
diff --git a/t/t1009-read-tree-new-index.sh b/t/t1009-read-tree-new-index.sh
new file mode 100755
index 0000000000..59b3aa4bc4
--- /dev/null
+++ b/t/t1009-read-tree-new-index.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+test_description='test read-tree into a fresh index file'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ echo one >a &&
+ git add a &&
+ git commit -m initial
+'
+
+test_expect_success 'non-existent index file' '
+ rm -f new-index &&
+ GIT_INDEX_FILE=new-index git read-tree master
+'
+
+test_expect_success 'empty index file' '
+ rm -f new-index &&
+ > new-index &&
+ GIT_INDEX_FILE=new-index git read-tree master
+'
+
+test_done
+
diff --git a/t/t2000-checkout-cache-clash.sh b/t/t2000-checkout-cache-clash.sh
index f7e1a735ec..de3edb5d57 100755
--- a/t/t2000-checkout-cache-clash.sh
+++ b/t/t2000-checkout-cache-clash.sh
@@ -48,4 +48,13 @@ test_expect_success \
'git checkout-index conflicting paths.' \
'test -f path0 && test -d path1 && test -f path1/file1'
+test_expect_success SYMLINKS 'checkout-index -f twice with --prefix' '
+ mkdir -p tar/get &&
+ ln -s tar/get there &&
+ echo first &&
+ git checkout-index -a -f --prefix=there/ &&
+ echo second &&
+ git checkout-index -a -f --prefix=there/
+'
+
test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index c5c29ccc4f..4e6a44b623 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -3,9 +3,10 @@
# Copyright (c) 2005 Amos Waterland
#
-test_description='git rebase should not destroy author information
+test_description='git rebase assorted tests
-This test runs git rebase and checks that the author information is not lost.
+This test runs git rebase and checks that the author information is not lost
+among other things.
'
. ./test-lib.sh
@@ -133,4 +134,25 @@ test_expect_success 'rebase -q is quiet' '
test ! -s output.out
'
+q_to_cr () {
+ tr Q '\015'
+}
+
+test_expect_success 'Rebase a commit that sprinkles CRs in' '
+ (
+ echo "One"
+ echo "TwoQ"
+ echo "Three"
+ echo "FQur"
+ echo "Five"
+ ) | q_to_cr >CR &&
+ git add CR &&
+ test_tick &&
+ git commit -a -m "A file with a line with CR" &&
+ git tag file-with-cr &&
+ git checkout HEAD^0 &&
+ git rebase --onto HEAD^^ HEAD^ &&
+ git diff --exit-code file-with-cr:CR HEAD:CR
+'
+
test_done
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index fd2a55a5c2..62fd65e18d 100755
--- a/t/t3701-add-interactive.sh
+++ b/t/t3701-add-interactive.sh
@@ -163,6 +163,17 @@ test_expect_success FILEMODE 'stage mode but not hunk' '
git diff file | grep "+content"
'
+
+test_expect_success FILEMODE 'stage mode and hunk' '
+ git reset --hard &&
+ echo content >>file &&
+ chmod +x file &&
+ printf "y\\ny\\n" | git add -p &&
+ git diff --cached file | grep "new mode" &&
+ git diff --cached file | grep "+content" &&
+ test -z "$(git diff file)"
+'
+
# end of tests disabled when filemode is not usable
test_expect_success 'setup again' '
diff --git a/t/t4013-diff-various.sh b/t/t4013-diff-various.sh
index 8b33321f8c..8e3694ed5b 100755
--- a/t/t4013-diff-various.sh
+++ b/t/t4013-diff-various.sh
@@ -207,6 +207,7 @@ log --root --cc --patch-with-stat --summary master
log -SF master
log -SF -p master
log --decorate --all
+log --decorate=full --all
rev-list --parents HEAD
rev-list --children HEAD
diff --git a/t/t4013/diff.log_--decorate=full_--all b/t/t4013/diff.log_--decorate=full_--all
new file mode 100644
index 0000000000..903d9d9659
--- /dev/null
+++ b/t/t4013/diff.log_--decorate=full_--all
@@ -0,0 +1,34 @@
+$ git log --decorate=full --all
+commit 59d314ad6f356dd08601a4cd5e530381da3e3c64 (refs/heads/master)
+Merge: 9a6d494 c7a2ab9
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:04:00 2006 +0000
+
+ Merge branch 'side'
+
+commit c7a2ab9e8eac7b117442a607d5a9b3950ae34d5a (refs/heads/side)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:03:00 2006 +0000
+
+ Side
+
+commit 9a6d4949b6b76956d9d5e26f2791ec2ceff5fdc0
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:02:00 2006 +0000
+
+ Third
+
+commit 1bde4ae5f36c8d9abe3a0fce0c6aab3c4a12fe44
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:01:00 2006 +0000
+
+ Second
+
+ This is the second commit.
+
+commit 444ac553ac7612cc88969031b02b3767fb8a353a (refs/heads/initial)
+Author: A U Thor <author@example.com>
+Date: Mon Jun 26 00:00:00 2006 +0000
+
+ Initial
+$
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
new file mode 100755
index 0000000000..9d9498bd95
--- /dev/null
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='diff with assume-unchanged entries'
+
+. ./test-lib.sh
+
+# external diff has been tested in t4020-diff-external.sh
+
+test_expect_success 'setup' '
+ echo zero > zero &&
+ git add zero &&
+ git commit -m zero &&
+ echo one > one &&
+ echo two > two &&
+ git add one two &&
+ git commit -m onetwo &&
+ git update-index --assume-unchanged one &&
+ echo borked >> one &&
+ test "$(git ls-files -v one)" = "h one"
+'
+
+test_expect_success 'diff-index does not examine assume-unchanged entries' '
+ git diff-index HEAD^ -- one | grep -q 5626abf0f72e58d7a153368ba57db4c673c0e171
+'
+
+test_expect_success 'diff-files does not examine assume-unchanged entries' '
+ rm one &&
+ test -z "$(git diff-files -- one)"
+'
+
+test_done
diff --git a/t/t4107-apply-ignore-whitespace.sh b/t/t4107-apply-ignore-whitespace.sh
new file mode 100755
index 0000000000..484654d6e4
--- /dev/null
+++ b/t/t4107-apply-ignore-whitespace.sh
@@ -0,0 +1,185 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Giuseppe Bilotta
+#
+
+test_description='git-apply --ignore-whitespace.
+
+'
+. ./test-lib.sh
+
+# This primes main.c file that indents without using HT at all.
+# Various patches with HT and other spaces are attempted in the test.
+
+cat > patch1.patch <<\EOF
+diff --git a/main.c b/main.c
+new file mode 100644
+--- /dev/null
++++ b/main.c
+@@ -0,0 +1,22 @@
++#include <stdio.h>
++
++void print_int(int num);
++int func(int num);
++
++int main() {
++ int i;
++
++ for (i = 0; i < 10; i++) {
++ print_int(func(i)); /* stuff */
++ }
++
++ return 0;
++}
++
++int func(int num) {
++ return num * num;
++}
++
++void print_int(int num) {
++ printf("%d", num);
++}
+EOF
+
+# Since whitespace is very significant and we want to prevent whitespace
+# mangling when creating this test from a patch, we protect 'fixable'
+# whitespace by replacing spaces with Z and replacing them at patch
+# creation time, hence the sed trick.
+
+# This patch will fail unless whitespace differences are being ignored
+
+sed -e 's/Z/ /g' > patch2.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -10,6 +10,8 @@
+Z print_int(func(i)); /* stuff */
+Z }
+Z
++ printf("\n");
++
+Z return 0;
+Z}
+Z
+EOF
+
+# This patch will fail even if whitespace differences are being ignored,
+# because of the missing string at EOL. TODO: this testcase should be
+# improved by creating a line that has the same hash with and without
+# the final string.
+
+sed -e 's/Z/ /g' > patch3.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -10,3 +10,4 @@
+Z for (i = 0; i < 10; i++) {
+Z print_int(func(i));Z
++ /* stuff */
+Z }
+EOF
+
+# This patch will fail even if whitespace differences are being ignored,
+# because of the missing EOL at EOF.
+
+sed -e 's/Z/ /g' > patch4.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -21,1 +21,1 @@
+- };Z
+\ No newline at end of file
++ };
+EOF
+
+# This patch will fail unless whitespace differences are being ignored.
+
+sed -e 's/Z/ /g' > patch5.patch <<\EOF
+diff --git a/main.c b/main.c
+--- a/main.c
++++ b/main.c
+@@ -2,2 +2,3 @@
+Z void print_int(int num);
++ /* a comment */
+Z int func(int num);
+EOF
+
+# And this is how the final output should be. Patches introduce
+# HTs but the original SP indents are mostly kept.
+
+sed -e 's/T/ /g' > main.c.final <<\EOF
+#include <stdio.h>
+
+void print_int(int num);
+T/* a comment */
+int func(int num);
+
+int main() {
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ print_int(func(i)); /* stuff */
+ }
+
+Tprintf("\n");
+
+ return 0;
+}
+
+int func(int num) {
+ return num * num;
+}
+
+void print_int(int num) {
+ printf("%d", num);
+}
+EOF
+
+test_expect_success 'file creation' '
+ git-apply patch1.patch
+'
+
+test_expect_success 'patch2 fails (retab)' '
+ test_must_fail git-apply patch2.patch
+'
+
+test_expect_success 'patch2 applies with --ignore-whitespace' '
+ git-apply --ignore-whitespace patch2.patch
+'
+
+test_expect_success 'patch2 reverse applies with --ignore-space-change' '
+ git-apply -R --ignore-space-change patch2.patch
+'
+
+git config apply.ignorewhitespace change
+
+test_expect_success 'patch2 applies (apply.ignorewhitespace = change)' '
+ git-apply patch2.patch
+'
+
+test_expect_success 'patch3 fails (missing string at EOL)' '
+ test_must_fail git-apply patch3.patch
+'
+
+test_expect_success 'patch4 fails (missing EOL at EOF)' '
+ test_must_fail git-apply patch4.patch
+'
+
+test_expect_success 'patch5 applies (leading whitespace)' '
+ git-apply patch5.patch
+'
+
+test_expect_success 'patches do not mangle whitespace' '
+ test_cmp main.c main.c.final
+'
+
+test_expect_success 're-create file (with --ignore-whitespace)' '
+ rm -f main.c &&
+ git-apply patch1.patch
+'
+
+test_expect_success 'patch5 fails (--no-ignore-whitespace)' '
+ test_must_fail git-apply --no-ignore-whitespace patch5.patch
+'
+
+test_done
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
new file mode 100644
index 0000000000..65d8d474bc
--- /dev/null
+++ b/t/t5531-deep-submodule-push.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+test_description='unpack-objects'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ mkdir pub.git &&
+ GIT_DIR=pub.git git init --bare
+ GIT_DIR=pub.git git config receive.fsckobjects true &&
+ mkdir work &&
+ (
+ cd work &&
+ git init &&
+ mkdir -p gar/bage &&
+ (
+ cd gar/bage &&
+ git init &&
+ >junk &&
+ git add junk &&
+ git commit -m "Initial junk"
+ ) &&
+ git add gar/bage &&
+ git commit -m "Initial superproject"
+ )
+'
+
+test_expect_success push '
+ (
+ cd work &&
+ git push ../pub.git master
+ )
+'
+
+test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 1315bab595..def397c53a 100755
--- a/t/t6030-bisect-porcelain.sh
+++ b/t/t6030-bisect-porcelain.sh
@@ -175,7 +175,7 @@ test_expect_success 'bisect skip: successfull result' '
git bisect start $HASH4 $HASH1 &&
git bisect skip &&
git bisect bad > my_bisect_log.txt &&
- grep "$HASH2 is first bad commit" my_bisect_log.txt &&
+ grep "$HASH2 is the first bad commit" my_bisect_log.txt &&
git bisect reset
'
@@ -261,7 +261,7 @@ test_expect_success \
git bisect good $HASH1 &&
git bisect bad $HASH4 &&
git bisect run ./test_script.sh > my_bisect_log.txt &&
- grep "$HASH3 is first bad commit" my_bisect_log.txt &&
+ grep "$HASH3 is the first bad commit" my_bisect_log.txt &&
git bisect reset'
# We want to automatically find the commit that
@@ -274,7 +274,7 @@ test_expect_success \
chmod +x test_script.sh &&
git bisect start $HASH4 $HASH1 &&
git bisect run ./test_script.sh > my_bisect_log.txt &&
- grep "$HASH4 is first bad commit" my_bisect_log.txt &&
+ grep "$HASH4 is the first bad commit" my_bisect_log.txt &&
git bisect reset'
# $HASH1 is good, $HASH5 is bad, we skip $HASH3
@@ -287,14 +287,14 @@ test_expect_success 'bisect skip: add line and then a new test' '
git bisect start $HASH5 $HASH1 &&
git bisect skip &&
git bisect good > my_bisect_log.txt &&
- grep "$HASH5 is first bad commit" my_bisect_log.txt &&
+ grep "$HASH5 is the first bad commit" my_bisect_log.txt &&
git bisect log > log_to_replay.txt &&
git bisect reset
'
test_expect_success 'bisect skip and bisect replay' '
git bisect replay log_to_replay.txt > my_bisect_log.txt &&
- grep "$HASH5 is first bad commit" my_bisect_log.txt &&
+ grep "$HASH5 is the first bad commit" my_bisect_log.txt &&
git bisect reset
'
@@ -335,7 +335,7 @@ test_expect_success 'bisect run & skip: find first bad' '
chmod +x test_script.sh &&
git bisect start $HASH7 $HASH1 &&
git bisect run ./test_script.sh > my_bisect_log.txt &&
- grep "$HASH6 is first bad commit" my_bisect_log.txt
+ grep "$HASH6 is the first bad commit" my_bisect_log.txt
'
test_expect_success 'bisect skip only one range' '
@@ -385,7 +385,7 @@ test_expect_success 'bisect does not create a "bisect" branch' '
rev_hash6=$(git rev-parse --verify HEAD) &&
test "$rev_hash6" = "$HASH6" &&
git bisect good > my_bisect_log.txt &&
- grep "$HASH7 is first bad commit" my_bisect_log.txt &&
+ grep "$HASH7 is the first bad commit" my_bisect_log.txt &&
git bisect reset &&
rev_hash6=$(git rev-parse --verify bisect) &&
test "$rev_hash6" = "$HASH6" &&
@@ -534,7 +534,7 @@ test_expect_success 'restricting bisection on one dir' '
para1=$(git rev-parse --verify HEAD) &&
test "$para1" = "$PARA_HASH1" &&
git bisect bad > my_bisect_log.txt &&
- grep "$PARA_HASH1 is first bad commit" my_bisect_log.txt
+ grep "$PARA_HASH1 is the first bad commit" my_bisect_log.txt
'
test_expect_success 'restricting bisection on one dir and a file' '
@@ -552,7 +552,7 @@ test_expect_success 'restricting bisection on one dir and a file' '
para1=$(git rev-parse --verify HEAD) &&
test "$para1" = "$PARA_HASH1" &&
git bisect good > my_bisect_log.txt &&
- grep "$PARA_HASH4 is first bad commit" my_bisect_log.txt
+ grep "$PARA_HASH4 is the first bad commit" my_bisect_log.txt
'
test_expect_success 'skipping away from skipped commit' '
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
new file mode 100755
index 0000000000..8b8bd81c09
--- /dev/null
+++ b/t/t6050-replace.sh
@@ -0,0 +1,200 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Christian Couder
+#
+test_description='Tests replace refs functionality'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+add_and_commit_file()
+{
+ _file="$1"
+ _msg="$2"
+
+ git add $_file || return $?
+ test_tick || return $?
+ git commit --quiet -m "$_file: $_msg"
+}
+
+HASH1=
+HASH2=
+HASH3=
+HASH4=
+HASH5=
+HASH6=
+HASH7=
+
+test_expect_success 'set up buggy branch' '
+ echo "line 1" >> hello &&
+ echo "line 2" >> hello &&
+ echo "line 3" >> hello &&
+ echo "line 4" >> hello &&
+ add_and_commit_file hello "4 lines" &&
+ HASH1=$(git rev-parse --verify HEAD) &&
+ echo "line BUG" >> hello &&
+ echo "line 6" >> hello &&
+ echo "line 7" >> hello &&
+ echo "line 8" >> hello &&
+ add_and_commit_file hello "4 more lines with a BUG" &&
+ HASH2=$(git rev-parse --verify HEAD) &&
+ echo "line 9" >> hello &&
+ echo "line 10" >> hello &&
+ add_and_commit_file hello "2 more lines" &&
+ HASH3=$(git rev-parse --verify HEAD) &&
+ echo "line 11" >> hello &&
+ add_and_commit_file hello "1 more line" &&
+ HASH4=$(git rev-parse --verify HEAD) &&
+ sed -e "s/BUG/5/" hello > hello.new &&
+ mv hello.new hello &&
+ add_and_commit_file hello "BUG fixed" &&
+ HASH5=$(git rev-parse --verify HEAD) &&
+ echo "line 12" >> hello &&
+ echo "line 13" >> hello &&
+ add_and_commit_file hello "2 more lines" &&
+ HASH6=$(git rev-parse --verify HEAD)
+ echo "line 14" >> hello &&
+ echo "line 15" >> hello &&
+ echo "line 16" >> hello &&
+ add_and_commit_file hello "again 3 more lines" &&
+ HASH7=$(git rev-parse --verify HEAD)
+'
+
+test_expect_success 'replace the author' '
+ git cat-file commit $HASH2 | grep "author A U Thor" &&
+ R=$(git cat-file commit $HASH2 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+ git cat-file commit $R | grep "author O Thor" &&
+ git update-ref refs/replace/$HASH2 $R &&
+ git show HEAD~5 | grep "O Thor" &&
+ git show $HASH2 | grep "O Thor"
+'
+
+cat >tag.sig <<EOF
+object $HASH2
+type commit
+tag mytag
+tagger T A Gger <> 0 +0000
+
+EOF
+
+test_expect_success 'tag replaced commit' '
+ git mktag <tag.sig >.git/refs/tags/mytag 2>message
+'
+
+test_expect_success '"git fsck" works' '
+ git fsck master > fsck_master.out &&
+ grep "dangling commit $R" fsck_master.out &&
+ grep "dangling tag $(cat .git/refs/tags/mytag)" fsck_master.out &&
+ test -z "$(git fsck)"
+'
+
+test_expect_success 'repack, clone and fetch work' '
+ git repack -a -d &&
+ git clone --no-hardlinks . clone_dir &&
+ cd clone_dir &&
+ git show HEAD~5 | grep "A U Thor" &&
+ git show $HASH2 | grep "A U Thor" &&
+ git cat-file commit $R &&
+ git repack -a -d &&
+ test_must_fail git cat-file commit $R &&
+ git fetch ../ "refs/replace/*:refs/replace/*" &&
+ git show HEAD~5 | grep "O Thor" &&
+ git show $HASH2 | grep "O Thor" &&
+ git cat-file commit $R &&
+ cd ..
+'
+
+test_expect_success '"git replace" listing and deleting' '
+ test "$HASH2" = "$(git replace -l)" &&
+ test "$HASH2" = "$(git replace)" &&
+ aa=${HASH2%??????????????????????????????????????} &&
+ test "$HASH2" = "$(git replace -l "$aa*")" &&
+ test_must_fail git replace -d $R &&
+ test_must_fail git replace -d &&
+ test_must_fail git replace -l -d $HASH2 &&
+ git replace -d $HASH2 &&
+ git show $HASH2 | grep "A U Thor" &&
+ test -z "$(git replace -l)"
+'
+
+test_expect_success '"git replace" replacing' '
+ git replace $HASH2 $R &&
+ git show $HASH2 | grep "O Thor" &&
+ test_must_fail git replace $HASH2 $R &&
+ git replace -f $HASH2 $R &&
+ test_must_fail git replace -f &&
+ test "$HASH2" = "$(git replace)"
+'
+
+# This creates a side branch where the bug in H2
+# does not appear because P2 is created by applying
+# H2 and squashing H5 into it.
+# P3, P4 and P6 are created by cherry-picking H3, H4
+# and H6 respectively.
+#
+# At this point, we should have the following:
+#
+# P2--P3--P4--P6
+# /
+# H1-H2-H3-H4-H5-H6-H7
+#
+# Then we replace H6 with P6.
+#
+test_expect_success 'create parallel branch without the bug' '
+ git replace -d $HASH2 &&
+ git show $HASH2 | grep "A U Thor" &&
+ git checkout $HASH1 &&
+ git cherry-pick $HASH2 &&
+ git show $HASH5 | git apply &&
+ git commit --amend -m "hello: 4 more lines WITHOUT the bug" hello &&
+ PARA2=$(git rev-parse --verify HEAD) &&
+ git cherry-pick $HASH3 &&
+ PARA3=$(git rev-parse --verify HEAD) &&
+ git cherry-pick $HASH4 &&
+ PARA4=$(git rev-parse --verify HEAD) &&
+ git cherry-pick $HASH6 &&
+ PARA6=$(git rev-parse --verify HEAD) &&
+ git replace $HASH6 $PARA6 &&
+ git checkout master &&
+ cur=$(git rev-parse --verify HEAD) &&
+ test "$cur" = "$HASH7" &&
+ git log --pretty=oneline | grep $PARA2 &&
+ git remote add cloned ./clone_dir
+'
+
+test_expect_success 'push to cloned repo' '
+ git push cloned $HASH6^:refs/heads/parallel &&
+ cd clone_dir &&
+ git checkout parallel &&
+ git log --pretty=oneline | grep $PARA2 &&
+ cd ..
+'
+
+test_expect_success 'push branch with replacement' '
+ git cat-file commit $PARA3 | grep "author A U Thor" &&
+ S=$(git cat-file commit $PARA3 | sed -e "s/A U/O/" | git hash-object -t commit --stdin -w) &&
+ git cat-file commit $S | grep "author O Thor" &&
+ git replace $PARA3 $S &&
+ git show $HASH6~2 | grep "O Thor" &&
+ git show $PARA3 | grep "O Thor" &&
+ git push cloned $HASH6^:refs/heads/parallel2 &&
+ cd clone_dir &&
+ git checkout parallel2 &&
+ git log --pretty=oneline | grep $PARA3 &&
+ git show $PARA3 | grep "A U Thor" &&
+ cd ..
+'
+
+test_expect_success 'fetch branch with replacement' '
+ git branch tofetch $HASH6 &&
+ cd clone_dir &&
+ git fetch origin refs/heads/tofetch:refs/heads/parallel3
+ git log --pretty=oneline parallel3 | grep $PARA3
+ git show $PARA3 | grep "A U Thor"
+ cd ..
+'
+
+#
+#
+test_done
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 61498293b9..6cc16c39fe 100755
--- a/t/t7401-submodule-summary.sh
+++ b/t/t7401-submodule-summary.sh
@@ -56,6 +56,15 @@ test_expect_success 'modified submodule(forward)' "
EOF
"
+test_expect_success 'modified submodule(forward), --files' "
+ git submodule summary --files >actual &&
+ diff actual - <<-EOF
+* sm1 $head1...$head2 (1):
+ > Add foo3
+
+EOF
+"
+
commit_file sm1 &&
cd sm1 &&
git reset --hard HEAD~2 >/dev/null &&
@@ -114,6 +123,15 @@ test_expect_success 'typechanged submodule(submodule->blob), --cached' "
EOF
"
+test_expect_success 'typechanged submodule(submodule->blob), --files' "
+ git submodule summary --files >actual &&
+ diff actual - <<-EOF
+* sm1 $head5(blob)->$head4(submodule) (3):
+ > Add foo5
+
+EOF
+"
+
rm -rf sm1 &&
git checkout-index sm1
test_expect_success 'typechanged submodule(submodule->blob)' "
@@ -205,4 +223,8 @@ test_expect_success '--for-status' "
EOF
"
+test_expect_success 'fail when using --files together with --cached' "
+ test_must_fail git submodule summary --files --cached
+"
+
test_done
diff --git a/t/t7406-submodule-reference.sh b/t/t7408-submodule-reference.sh
index cc16d3f05d..cc16d3f05d 100755
--- a/t/t7406-submodule-reference.sh
+++ b/t/t7408-submodule-reference.sh
diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh
index f159ab689b..9d9ebd533c 100755
--- a/t/t9120-git-svn-clone-with-percent-escapes.sh
+++ b/t/t9120-git-svn-clone-with-percent-escapes.sh
@@ -10,6 +10,10 @@ test_expect_success 'setup svnrepo' '
mkdir project project/trunk project/branches project/tags &&
echo foo > project/trunk/foo &&
svn_cmd import -m "$test_description" project "$svnrepo/pr ject" &&
+ svn_cmd cp -m "branch" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/branches/b" &&
+ svn_cmd cp -m "tag" "$svnrepo/pr ject/trunk" \
+ "$svnrepo/pr ject/tags/v1" &&
rm -rf project &&
start_httpd
'
@@ -21,6 +25,54 @@ test_expect_success 'test clone with percent escapes' '
cd ..
'
+# SVN works either way, so should we...
+
+test_expect_success 'svn checkout with percent escapes' '
+ svn_cmd checkout "$svnrepo/pr%20ject" svn.percent &&
+ svn_cmd checkout "$svnrepo/pr%20ject/trunk" svn.percent.trunk
+'
+
+test_expect_success 'svn checkout with space' '
+ svn_cmd checkout "$svnrepo/pr ject" svn.space &&
+ svn_cmd checkout "$svnrepo/pr ject/trunk" svn.space.trunk
+'
+
+test_expect_success 'test clone trunk with percent escapes and minimize-url' '
+ git svn clone --minimize-url "$svnrepo/pr%20ject/trunk" minimize &&
+ (
+ cd minimize &&
+ git rev-parse refs/${remotes_git_svn}
+ )
+'
+
+test_expect_success 'test clone trunk with percent escapes' '
+ git svn clone "$svnrepo/pr%20ject/trunk" trunk &&
+ (
+ cd trunk &&
+ git rev-parse refs/${remotes_git_svn}
+ )
+'
+
+test_expect_success 'test clone --stdlayout with percent escapes' '
+ git svn clone --stdlayout "$svnrepo/pr%20ject" percent &&
+ (
+ cd percent &&
+ git rev-parse refs/remotes/trunk^0 &&
+ git rev-parse refs/remotes/b^0 &&
+ git rev-parse refs/remotes/tags/v1^0
+ )
+'
+
+test_expect_success 'test clone -s with unescaped space' '
+ git svn clone -s "$svnrepo/pr ject" space &&
+ (
+ cd space &&
+ git rev-parse refs/remotes/trunk^0 &&
+ git rev-parse refs/remotes/b^0 &&
+ git rev-parse refs/remotes/tags/v1^0
+ )
+'
+
stop_httpd
test_done
diff --git a/upload-pack.c b/upload-pack.c
index f7d308a411..4d8be834ff 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -651,6 +651,7 @@ int main(int argc, char **argv)
int strict = 0;
git_extract_argv0_path(argv[0]);
+ read_replace_refs = 0;
for (i = 1; i < argc; i++) {
char *arg = argv[i];