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.txt26
-rw-r--r--Documentation/fetch-options.txt2
-rw-r--r--Documentation/git-add.txt2
-rw-r--r--Documentation/git-am.txt5
-rw-r--r--Documentation/git-apply.txt13
-rw-r--r--Documentation/git-archive.txt2
-rw-r--r--Documentation/git-branch.txt6
-rw-r--r--Documentation/git-clean.txt3
-rw-r--r--Documentation/git-clone.txt22
-rw-r--r--Documentation/git-filter-branch.txt10
-rw-r--r--Documentation/git-gc.txt2
-rw-r--r--Documentation/git-grep.txt5
-rw-r--r--Documentation/git-init-db.txt2
-rw-r--r--Documentation/git-init.txt5
-rw-r--r--Documentation/git-instaweb.txt2
-rw-r--r--Documentation/git-log.txt8
-rw-r--r--Documentation/git-ls-files.txt2
-rw-r--r--Documentation/git-merge-base.txt9
-rw-r--r--Documentation/git-merge.txt2
-rw-r--r--Documentation/git-prune-packed.txt4
-rw-r--r--Documentation/git-push.txt86
-rw-r--r--Documentation/git-read-tree.txt5
-rw-r--r--Documentation/git-rebase.txt3
-rw-r--r--Documentation/git-remote.txt4
-rw-r--r--Documentation/git-replace.txt71
-rw-r--r--Documentation/git-rev-list.txt24
-rw-r--r--Documentation/git-show-branch.txt13
-rw-r--r--Documentation/git-stash.txt17
-rw-r--r--Documentation/git-submodule.txt36
-rw-r--r--Documentation/git-svn.txt6
-rw-r--r--Documentation/git-symbolic-ref.txt4
-rw-r--r--Documentation/git-tag.txt19
-rw-r--r--Documentation/git-verify-pack.txt11
-rw-r--r--Documentation/git-write-tree.txt3
-rw-r--r--Documentation/git.txt5
-rw-r--r--Documentation/gitattributes.txt2
-rw-r--r--Documentation/gitcore-tutorial.txt4
-rw-r--r--Documentation/technical/api-run-command.txt31
-rw-r--r--Documentation/technical/api-tree-walking.txt147
-rw-r--r--Documentation/user-manual.txt2
-rw-r--r--Makefile38
-rw-r--r--abspath.c6
-rw-r--r--arm/sha1.c82
-rw-r--r--arm/sha1.h23
-rw-r--r--arm/sha1_arm.S183
-rw-r--r--bisect.c2
-rw-r--r--block-sha1/sha1.c282
-rw-r--r--block-sha1/sha1.h22
-rw-r--r--builtin-apply.c255
-rw-r--r--builtin-clean.c7
-rw-r--r--builtin-clone.c11
-rw-r--r--builtin-describe.c5
-rw-r--r--builtin-diff.c2
-rw-r--r--builtin-fsck.c1
-rw-r--r--builtin-grep.c56
-rw-r--r--builtin-init-db.c86
-rw-r--r--builtin-log.c34
-rw-r--r--builtin-mailinfo.c8
-rw-r--r--builtin-mailsplit.c45
-rw-r--r--builtin-merge-base.c2
-rw-r--r--builtin-merge.c22
-rw-r--r--builtin-pack-objects.c4
-rw-r--r--builtin-prune-packed.c29
-rw-r--r--builtin-prune.c1
-rw-r--r--builtin-push.c10
-rw-r--r--builtin-read-tree.c176
-rw-r--r--builtin-receive-pack.c38
-rw-r--r--builtin-reflog.c2
-rw-r--r--builtin-replace.c159
-rw-r--r--builtin-send-pack.c3
-rw-r--r--builtin-show-branch.c6
-rw-r--r--builtin-unpack-objects.c18
-rw-r--r--builtin-verify-pack.c110
-rw-r--r--builtin-verify-tag.c21
-rw-r--r--builtin-write-tree.c40
-rw-r--r--builtin.h2
-rw-r--r--cache.h11
-rw-r--r--commit.c10
-rw-r--r--commit.h3
-rw-r--r--compat/bswap.h36
-rw-r--r--compat/mingw.h5
-rw-r--r--compat/snprintf.c3
-rw-r--r--config.c10
-rw-r--r--configure.ac10
-rwxr-xr-xcontrib/completion/git-completion.bash4
-rw-r--r--contrib/emacs/git.el13
-rwxr-xr-xcontrib/fast-import/git-p4169
-rwxr-xr-xcontrib/hg-to-git/hg-to-git.py2
-rw-r--r--convert.c2
-rw-r--r--date.c2
-rw-r--r--diff-lib.c6
-rw-r--r--dir.c12
-rw-r--r--dir.h5
-rw-r--r--entry.c19
-rw-r--r--environment.c2
-rwxr-xr-xgit-add--interactive.perl4
-rwxr-xr-xgit-am.sh32
-rwxr-xr-xgit-bisect.sh2
-rw-r--r--git-compat-util.h4
-rwxr-xr-xgit-cvsimport.perl39
-rwxr-xr-xgit-filter-branch.sh12
-rwxr-xr-xgit-instaweb.sh59
-rwxr-xr-xgit-pull.sh32
-rwxr-xr-xgit-rebase.sh9
-rwxr-xr-xgit-send-email.perl1
-rwxr-xr-xgit-stash.sh27
-rwxr-xr-xgit-submodule.sh96
-rwxr-xr-xgit-svn.perl110
-rw-r--r--git.c11
-rw-r--r--gitk-git/gitk31
-rw-r--r--gitweb/INSTALL9
-rw-r--r--gitweb/README6
-rw-r--r--gitweb/git-favicon.pngbin164 -> 115 bytes
-rw-r--r--gitweb/git-logo.pngbin208 -> 207 bytes
-rw-r--r--gitweb/gitweb.css24
-rwxr-xr-xgitweb/gitweb.perl91
-rw-r--r--graph.c19
-rw-r--r--grep.h1
-rw-r--r--help.c4
-rw-r--r--http.c33
-rw-r--r--ll-merge.c4
-rw-r--r--log-tree.c19
-rw-r--r--log-tree.h2
-rw-r--r--merge-recursive.c28
-rw-r--r--mktag.c7
-rw-r--r--mozilla-sha1/sha1.c151
-rw-r--r--mozilla-sha1/sha1.h50
-rw-r--r--object.c9
-rw-r--r--parse-options.c8
-rw-r--r--parse-options.h4
-rw-r--r--pretty.c14
-rw-r--r--refs.c7
-rw-r--r--refs.h1
-rw-r--r--replace_object.c114
-rw-r--r--revision.c19
-rw-r--r--revision.h4
-rw-r--r--run-command.c105
-rw-r--r--run-command.h13
-rw-r--r--send-pack.h1
-rw-r--r--sha1_file.c23
-rw-r--r--strbuf.c15
-rw-r--r--strbuf.h1
-rw-r--r--symlinks.c4
-rw-r--r--t/Makefile2
-rw-r--r--t/lib-git-svn.sh2
-rwxr-xr-xt/t0001-init.sh83
-rwxr-xr-xt/t1009-read-tree-new-index.sh25
-rwxr-xr-xt/t1300-repo-config.sh5
-rwxr-xr-xt/t2000-checkout-cache-clash.sh9
-rwxr-xr-xt/t3400-rebase.sh26
-rwxr-xr-xt/t3404-rebase-interactive.sh2
-rwxr-xr-xt/t3409-rebase-preserve-merges.sh2
-rwxr-xr-xt/t3411-rebase-preserve-around-merges.sh2
-rwxr-xr-xt/t3414-rebase-preserve-onto.sh2
-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/t4014-format-patch.sh3
-rwxr-xr-xt/t4020-diff-external.sh2
-rwxr-xr-xt/t4039-diff-assume-unchanged.sh31
-rwxr-xr-xt/t4107-apply-ignore-whitespace.sh185
-rwxr-xr-xt/t4132-apply-removal.sh95
-rwxr-xr-xt/t4150-am.sh15
-rwxr-xr-xt/t4202-log.sh4
-rwxr-xr-xt/t5510-fetch.sh11
-rwxr-xr-xt/t5520-pull.sh24
-rwxr-xr-xt/t5530-upload-pack-error.sh17
-rw-r--r--t/t5531-deep-submodule-push.sh35
-rwxr-xr-xt/t6010-merge-base.sh18
-rwxr-xr-xt/t6015-rev-list-show-all-parents.sh31
-rwxr-xr-xt/t6016-rev-list-graph-simplify-history.sh276
-rwxr-xr-xt/t6030-bisect-porcelain.sh18
-rwxr-xr-xt/t6035-merge-dir-to-symlink.sh93
-rwxr-xr-xt/t6036-recursive-corner-cases.sh55
-rwxr-xr-xt/t6050-replace.sh200
-rwxr-xr-xt/t7002-grep.sh51
-rwxr-xr-xt/t7300-clean.sh39
-rwxr-xr-xt/t7401-submodule-summary.sh22
-rwxr-xr-xt/t7407-submodule-foreach.sh237
-rwxr-xr-xt/t7408-submodule-reference.sh (renamed from t/t7406-submodule-reference.sh)0
-rwxr-xr-xt/t7608-merge-messages.sh60
-rwxr-xr-xt/t9101-git-svn-props.sh5
-rwxr-xr-xt/t9104-git-svn-follow-parent.sh10
-rwxr-xr-xt/t9107-git-svn-migrate.sh28
-rwxr-xr-xt/t9120-git-svn-clone-with-percent-escapes.sh52
-rwxr-xr-xt/t9135-git-svn-moved-branch-empty-file.sh7
-rwxr-xr-xt/t9143-git-svn-gc.sh10
-rwxr-xr-xt/t9144-git-svn-old-rev_map.sh31
-rwxr-xr-xt/t9145-git-svn-master-branch.sh25
-rw-r--r--t/test-lib.sh25
-rw-r--r--transport.c46
-rw-r--r--transport.h4
-rw-r--r--unpack-trees.h24
-rw-r--r--upload-pack.c50
196 files changed, 4714 insertions, 1377 deletions
diff --git a/.gitignore b/.gitignore
index 41c0b20..10808e3 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 0000000..e439e45
--- /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 c6f09f8..5256c7f 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].
@@ -605,7 +613,7 @@ color.interactive.<slot>::
Use customized color for 'git-add --interactive'
output. `<slot>` may be `prompt`, `header`, `help` or `error`, for
four distinct types of normal output from interactive
- programs. The values of these variables may be specified as
+ commands. The values of these variables may be specified as
in color.branch.<slot>.
color.pager::
@@ -1113,7 +1121,7 @@ instaweb.port::
linkgit:git-instaweb[1].
interactive.singlekey::
- In interactive programs, allow the user to provide one-letter
+ In interactive commands, allow the user to provide one-letter
input with a single key (i.e., without hitting enter).
Currently this is used only by the `\--patch` mode of
linkgit:git-add[1]. Note that this setting is silently
@@ -1218,12 +1226,20 @@ pack.compression::
pack.deltaCacheSize::
The maximum memory in bytes used for caching deltas in
- linkgit:git-pack-objects[1].
- A value of 0 means no limit. Defaults to 0.
+ linkgit:git-pack-objects[1] before writing them out to a pack.
+ This cache is used to speed up the writing object phase by not
+ having to recompute the final delta result once the best match
+ for all objects is found. Repacking large repositories on machines
+ which are tight with memory might be badly impacted by this though,
+ especially if this cache pushes the system into swapping.
+ A value of 0 means no limit. The smallest size of 1 byte may be
+ used to virtually disable this cache. Defaults to 256 MiB.
pack.deltaCacheLimit::
The maximum size of a delta, that is cached in
- linkgit:git-pack-objects[1]. Defaults to 1000.
+ linkgit:git-pack-objects[1]. This cache is used to speed up the
+ writing object phase by not having to recompute the final delta
+ result once the best match for all objects is found. Defaults to 1000.
pack.threads::
Specifies the number of threads to spawn when searching for best
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index d313795..ea3b1bc 100644
--- a/Documentation/fetch-options.txt
+++ b/Documentation/fetch-options.txt
@@ -1,7 +1,7 @@
-q::
--quiet::
Pass --quiet to git-fetch-pack and silence any other internally
- used programs.
+ used git commands.
-v::
--verbose::
diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index ab1943c..e67b7e8 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -10,7 +10,7 @@ SYNOPSIS
[verse]
'git add' [-n] [-v] [--force | -f] [--interactive | -i] [--patch | -p]
[--edit | -e] [--all | [--update | -u]] [--intent-to-add | -N]
- [--refresh] [--ignore-errors] [--] <filepattern>...
+ [--refresh] [--ignore-errors] [--] [<filepattern>...]
DESCRIPTION
-----------
diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt
index 32e689b..fcacc94 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 735374d..5ee8c91 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-archive.txt b/Documentation/git-archive.txt
index bc132c8..92444dd 100644
--- a/Documentation/git-archive.txt
+++ b/Documentation/git-archive.txt
@@ -9,7 +9,7 @@ git-archive - Create an archive of files from a named tree
SYNOPSIS
--------
[verse]
-'git archive' --format=<fmt> [--list] [--prefix=<prefix>/] [<extra>]
+'git archive' [--format=<fmt>] [--list] [--prefix=<prefix>/] [<extra>]
[--output=<file>] [--worktree-attributes]
[--remote=<repo> [--exec=<git-upload-archive>]] <tree-ish>
[path...]
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index ae201de..9998887 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-clean.txt b/Documentation/git-clean.txt
index be894af..ae8938b 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -27,6 +27,9 @@ OPTIONS
-------
-d::
Remove untracked directories in addition to untracked files.
+ If an untracked directory is managed by a different git
+ repository, it is not removed by default. Use -f option twice
+ if you really want to remove such a directory.
-f::
If the git configuration specifies clean.requireForce as true,
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index b14de6c..88ea272 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -12,7 +12,7 @@ SYNOPSIS
'git clone' [--template=<template_directory>]
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
[-o <name>] [-u <upload-pack>] [--reference <repository>]
- [--depth <depth>] [--] <repository> [<directory>]
+ [--depth <depth>] [--recursive] [--] <repository> [<directory>]
DESCRIPTION
-----------
@@ -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
@@ -139,6 +147,14 @@ then the cloned repository will become corrupt.
with a long history, and would want to send in fixes
as patches.
+--recursive::
+ After the clone is created, initialize all submodules within,
+ using their default settings. This is equivalent to running
+ 'git submodule update --init --recursive' immediately after
+ the clone is finished. This option is ignored if the cloned
+ repository does not have a worktree/checkout (i.e. if any of
+ `--no-checkout`/`-n`, `--bare`, or `--mirror` is given)
+
<repository>::
The (possibly remote) repository to clone from. See the
<<URLS,URLS>> section below for more information on specifying
diff --git a/Documentation/git-filter-branch.txt b/Documentation/git-filter-branch.txt
index ab527b5..32ea856 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-gc.txt b/Documentation/git-gc.txt
index b292e98..dcac8c8 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -61,7 +61,7 @@ automatic consolidation of packs.
--prune=<date>::
Prune loose objects older than date (default is 2 weeks ago,
- overrideable by the config variable `gc.pruneExpire`). This
+ overridable by the config variable `gc.pruneExpire`). This
option is on by default.
--no-prune::
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index b753c9d..8c70020 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -17,6 +17,7 @@ SYNOPSIS
[-l | --files-with-matches] [-L | --files-without-match]
[-z | --null]
[-c | --count] [--all-match]
+ [--max-depth <depth>]
[--color | --no-color]
[-A <post-context>] [-B <pre-context>] [-C <context>]
[-f <file>] [-e] <pattern>
@@ -47,6 +48,10 @@ OPTIONS
-I::
Don't match the pattern in binary files.
+--max-depth <depth>::
+ For each pathspec given on command line, descend at most <depth>
+ levels of directories. A negative value means no limit.
+
-w::
--word-regexp::
Match the pattern only at word boundary (either begin at the
diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt
index 1fd0ff2..eba3cb4 100644
--- a/Documentation/git-init-db.txt
+++ b/Documentation/git-init-db.txt
@@ -8,7 +8,7 @@ git-init-db - Creates an empty git repository
SYNOPSIS
--------
-'git init-db' [-q | --quiet] [--template=<template_directory>] [--shared[=<permissions>]]
+'git init-db' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]]
DESCRIPTION
diff --git a/Documentation/git-init.txt b/Documentation/git-init.txt
index 7151d12..f081b24 100644
--- a/Documentation/git-init.txt
+++ b/Documentation/git-init.txt
@@ -8,7 +8,7 @@ git-init - Create an empty git repository or reinitialize an existing one
SYNOPSIS
--------
-'git init' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]]
+'git init' [-q | --quiet] [--bare] [--template=<template_directory>] [--shared[=<permissions>]] [directory]
OPTIONS
@@ -74,6 +74,9 @@ By default, the configuration flag receive.denyNonFastForwards is enabled
in shared repositories, so that you cannot force a non fast-forwarding push
into it.
+If you name a (possibly non-existent) directory at the end of the command
+line, the command is run inside the directory (possibly after creating it).
+
--
diff --git a/Documentation/git-instaweb.txt b/Documentation/git-instaweb.txt
index 22da21a..0771f25 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 34cf4e5..3d79de1 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-ls-files.txt b/Documentation/git-ls-files.txt
index 057a021..021066e 100644
--- a/Documentation/git-ls-files.txt
+++ b/Documentation/git-ls-files.txt
@@ -44,7 +44,7 @@ OPTIONS
-o::
--others::
- Show other files in the output
+ Show other (i.e. untracked) files in the output
-i::
--ignored::
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
index 767486c..ce5b369 100644
--- a/Documentation/git-merge-base.txt
+++ b/Documentation/git-merge-base.txt
@@ -8,12 +8,12 @@ git-merge-base - Find as good common ancestors as possible for a merge
SYNOPSIS
--------
-'git merge-base' [--all] <commit> <commit>...
+'git merge-base' [-a|--all] <commit> <commit>...
DESCRIPTION
-----------
-'git-merge-base' finds best common ancestor(s) between two commits to use
+'git merge-base' finds best common ancestor(s) between two commits to use
in a three-way merge. One common ancestor is 'better' than another common
ancestor if the latter is an ancestor of the former. A common ancestor
that does not have any better common ancestor is a 'best common
@@ -27,8 +27,13 @@ commits on the command line. As the most common special case, specifying only
two commits on the command line means computing the merge base between
the given two commits.
+As a consequence, the 'merge base' is not necessarily contained in each of the
+commit arguments if more than two commits are specified. This is different
+from linkgit:git-show-branch[1] when used with the `--merge-base` option.
+
OPTIONS
-------
+-a::
--all::
Output all merge bases for the commits, instead of just one.
diff --git a/Documentation/git-merge.txt b/Documentation/git-merge.txt
index c04ae73..af68d69 100644
--- a/Documentation/git-merge.txt
+++ b/Documentation/git-merge.txt
@@ -10,7 +10,7 @@ SYNOPSIS
--------
[verse]
'git merge' [-n] [--stat] [--no-commit] [--squash] [-s <strategy>]...
- [-m <msg>] <remote> <remote>...
+ [-m <msg>] <remote>...
'git merge' <msg> HEAD <remote>...
DESCRIPTION
diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt
index b5f26ce..abfc6b6 100644
--- a/Documentation/git-prune-packed.txt
+++ b/Documentation/git-prune-packed.txt
@@ -8,7 +8,7 @@ git-prune-packed - Remove extra objects that are already in pack files
SYNOPSIS
--------
-'git prune-packed' [-n] [-q]
+'git prune-packed' [-n|--dry-run] [-q|--quiet]
DESCRIPTION
@@ -28,10 +28,12 @@ disk storage, etc.
OPTIONS
-------
-n::
+--dry-run::
Don't actually remove any objects, only show those that would have been
removed.
-q::
+--quiet::
Squelch the progress indicator.
Author
diff --git a/Documentation/git-push.txt b/Documentation/git-push.txt
index 2653388..58d2bd5 100644
--- a/Documentation/git-push.txt
+++ b/Documentation/git-push.txt
@@ -195,6 +195,92 @@ reason::
refs, no explanation is needed. For a failed ref, the reason for
failure is described.
+Note about fast-forwards
+------------------------
+
+When an update changes a branch (or more in general, a ref) that used to
+point at commit A to point at another commit B, it is called a
+fast-forward update if and only if B is a descendant of A.
+
+In a fast-forward update from A to B, the set of commits that the original
+commit A built on top of is a subset of the commits the new commit B
+builds on top of. Hence, it does not lose any history.
+
+In contrast, a non-fast-forward update will lose history. For example,
+suppose you and somebody else started at the same commit X, and you built
+a history leading to commit B while the other person built a history
+leading to commit A. The history looks like this:
+
+----------------
+
+ B
+ /
+ ---X---A
+
+----------------
+
+Further suppose that the other person already pushed changes leading to A
+back to the original repository you two obtained the original commit X.
+
+The push done by the other person updated the branch that used to point at
+commit X to point at commit A. It is a fast-forward.
+
+But if you try to push, you will attempt to update the branch (that
+now points at A) with commit B. This does _not_ fast-forward. If you did
+so, the changes introduced by commit A will be lost, because everybody
+will now start building on top of B.
+
+The command by default does not allow an update that is not a fast-forward
+to prevent such loss of history.
+
+If you do not want to lose your work (history from X to B) nor the work by
+the other person (history from X to A), you would need to first fetch the
+history from the repository, create a history that contains changes done
+by both parties, and push the result back.
+
+You can perform "git pull", resolve potential conflicts, and "git push"
+the result. A "git pull" will create a merge commit C between commits A
+and B.
+
+----------------
+
+ B---C
+ / /
+ ---X---A
+
+----------------
+
+Updating A with the resulting merge commit will fast-forward and your
+push will be accepted.
+
+Alternatively, you can rebase your change between X and B on top of A,
+with "git pull --rebase", and push the result back. The rebase will
+create a new commit D that builds the change between X and B on top of
+A.
+
+----------------
+
+ B D
+ / /
+ ---X---A
+
+----------------
+
+Again, updating A with this commit will fast-forward and your push will be
+accepted.
+
+There is another common situation where you may encounter non-fast-forward
+rejection when you try to push, and it is possible even when you are
+pushing into a repository nobody else pushes into. After you push commit
+A yourself (in the first picture in this section), replace it with "git
+commit --amend" to produce commit B, and you try to push it out, because
+forgot that you have pushed A out already. In such a case, and only if
+you are certain that nobody in the meantime fetched your earlier commit A
+(and started building on top of it), you can run "git push --force" to
+overwrite it. In other words, "git push --force" is a method reserved for
+a case where you do mean to lose history.
+
+
Examples
--------
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
index 7160fa1..4a932b0 100644
--- a/Documentation/git-read-tree.txt
+++ b/Documentation/git-read-tree.txt
@@ -8,7 +8,10 @@ git-read-tree - Reads tree information into the index
SYNOPSIS
--------
-'git read-tree' (<tree-ish> | [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
+'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]
+ [-u [--exclude-per-directory=<gitignore>] | -i]]
+ [--index-output=<file>]
+ <tree-ish1> [<tree-ish2> [<tree-ish3>]]
DESCRIPTION
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index db1b71d..0aefc34 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-remote.txt b/Documentation/git-remote.txt
index 9e2b4ea..82a3d29 100644
--- a/Documentation/git-remote.txt
+++ b/Documentation/git-remote.txt
@@ -114,14 +114,14 @@ These stale branches have already been removed from the remote repository
referenced by <name>, but are still locally available in
"remotes/<name>".
+
-With `--dry-run` option, report what branches will be pruned, but do no
+With `--dry-run` option, report what branches will be pruned, but do not
actually prune them.
'update'::
Fetch updates for a named set of remotes in the repository as defined by
remotes.<group>. If a named group is not specified on the command line,
-the configuration parameter remotes.default will get used; if
+the configuration parameter remotes.default will be used; if
remotes.default is not defined, all remotes which do not have the
configuration parameter remote.<name>.skipDefaultUpdate set to true will
be updated. (See linkgit:git-config[1]).
diff --git a/Documentation/git-replace.txt b/Documentation/git-replace.txt
new file mode 100644
index 0000000..915cb77
--- /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-rev-list.txt b/Documentation/git-rev-list.txt
index a765cfa..3341d1b 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -51,20 +51,26 @@ SYNOPSIS
DESCRIPTION
-----------
-Lists commit objects in reverse chronological order starting at the
-given commit(s), taking ancestry relationship into account. This is
-useful to produce human-readable log output.
+List commits that are reachable by following the `parent` links from the
+given commit(s), but exclude commits that are reachable from the one(s)
+given with a '{caret}' in front of them. The output is given in reverse
+chronological order by default.
-Commits which are stated with a preceding '{caret}' cause listing to
-stop at that point. Their parents are implied. Thus the following
-command:
+You can think of this as a set operation. Commits given on the command
+line form a set of commits that are reachable from any of them, and then
+commits reachable from any of the ones given with '{caret}' in front are
+subtracted from that set. The remaining commits are what comes out in the
+command's output. Various other options and paths parameters can be used
+to further limit the result.
+
+Thus, the following command:
-----------------------------------------------------------------------
$ git rev-list foo bar ^baz
-----------------------------------------------------------------------
-means "list all the commits which are included in 'foo' and 'bar', but
-not in 'baz'".
+means "list all the commits which are reachable from 'foo' or 'bar', but
+not from 'baz'".
A special notation "'<commit1>'..'<commit2>'" can be used as a
short-hand for "{caret}'<commit1>' '<commit2>'". For example, either of
@@ -84,7 +90,7 @@ between the two operands. The following two commands are equivalent:
$ git rev-list A...B
-----------------------------------------------------------------------
-'git-rev-list' is a very essential git program, since it
+'rev-list' is a very essential git command, since it
provides the ability to build and traverse commit ancestry graphs. For
this reason, it has a lot of different options that enables it to be
used by commands as different as 'git-bisect' and
diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index 89ec536..7343361 100644
--- a/Documentation/git-show-branch.txt
+++ b/Documentation/git-show-branch.txt
@@ -8,11 +8,12 @@ git-show-branch - Show branches and their commits
SYNOPSIS
--------
[verse]
-'git show-branch' [--all] [--remotes] [--topo-order | --date-order]
- [--current] [--color | --no-color]
+'git show-branch' [-a|--all] [-r|--remotes] [--topo-order | --date-order]
+ [--current] [--color | --no-color] [--sparse]
[--more=<n> | --list | --independent | --merge-base]
[--no-name | --sha1-name] [--topics]
[<rev> | <glob>]...
+
'git show-branch' (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]
DESCRIPTION
@@ -81,9 +82,11 @@ OPTIONS
Synonym to `--more=-1`
--merge-base::
- Instead of showing the commit list, just act like the
- 'git-merge-base -a' command, except that it can accept
- more than two heads.
+ Instead of showing the commit list, determine possible
+ merge bases for the specified commits. All merge bases
+ will be contained in all specified commits. This is
+ different from how linkgit:git-merge-base[1] handles
+ the case of three or more commits.
--independent::
Among the <reference>s given, display only the ones that
diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 1c64a02..2f5ca7b 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -114,7 +114,8 @@ no conflicts.
clear::
Remove all the stashed states. Note that those states will then
- be subject to pruning, and may be difficult or impossible to recover.
+ be subject to pruning, and may be impossible to recover (see
+ 'Examples' below for a possible strategy).
drop [-q|--quiet] [<stash>]::
@@ -217,6 +218,20 @@ $ edit/build/test remaining parts
$ git commit foo -m 'Remaining parts'
----------------------------------------------------------------
+Recovering stashes that were cleared/dropped erroneously::
+
+If you mistakenly drop or clear stashes, they cannot be recovered
+through the normal safety mechanisms. However, you can try the
+following incantation to get a list of stashes that are still in your
+repository, but not reachable any more:
++
+----------------------------------------------------------------
+git fsck --unreachable |
+grep commit | cut -d\ -f3 |
+xargs git log --merges --no-walk --grep=WIP
+----------------------------------------------------------------
+
+
SEE ALSO
--------
linkgit:git-checkout[1],
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index 683ba1a..5ccdd18 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -11,12 +11,12 @@ SYNOPSIS
[verse]
'git submodule' [--quiet] add [-b branch]
[--reference <repository>] [--] <repository> <path>
-'git submodule' [--quiet] status [--cached] [--] [<path>...]
+'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
'git submodule' [--quiet] init [--] [<path>...]
'git submodule' [--quiet] update [--init] [-N|--no-fetch] [--rebase]
- [--reference <repository>] [--] [<path>...]
-'git submodule' [--quiet] summary [--summary-limit <n>] [commit] [--] [<path>...]
-'git submodule' [--quiet] foreach <command>
+ [--reference <repository>] [--merge] [--recursive] [--] [<path>...]
+'git submodule' [--quiet] summary [--cached|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+'git submodule' [--quiet] foreach [--recursive] <command>
'git submodule' [--quiet] sync [--] [<path>...]
@@ -100,6 +100,9 @@ status::
initialized and `+` if the currently checked out submodule commit
does not match the SHA-1 found in the index of the containing
repository. This command is the default command for 'git-submodule'.
++
+If '--recursive' is specified, this command will recurse into nested
+submodules, and show their status as well.
init::
Initialize the submodules, i.e. register each submodule name
@@ -122,21 +125,31 @@ update::
If the submodule is not yet initialized, and you just want to use the
setting as stored in .gitmodules, you can automatically initialize the
submodule with the --init option.
++
+If '--recursive' is specified, this command will recurse into the
+registered submodules, and update any nested submodules within.
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.
- The command has access to the variables $path and $sha1:
+ The command has access to the variables $name, $path and $sha1:
+ $name is the name of the relevant submodule section in .gitmodules,
$path is the name of the submodule directory relative to the
superproject, and $sha1 is the commit as recorded in the superproject.
Any submodules defined in the superproject but not checked out are
ignored by this command. Unless given --quiet, foreach prints the name
of each submodule before evaluating the command.
+ If --recursive is given, submodules are traversed recursively (i.e.
+ the given shell command is evaluated in nested submodules as well).
A non-zero return from the command in any submodule causes
the processing to terminate. This can be overridden by adding '|| :'
to the end of the command.
@@ -169,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.
@@ -209,6 +227,12 @@ OPTIONS
*NOTE*: Do *not* use this option unless you have read the note
for linkgit:git-clone[1]'s --reference and --shared options carefully.
+--recursive::
+ This option is only valid for foreach, update and status commands.
+ Traverse submodules recursively. The operation is performed not
+ only in the submodules of the current repo, but also
+ in any nested submodules inside those submodules (and so on).
+
<path>...::
Paths to submodule(s). When specified this will restrict the command
to only operate on the submodules found at the specified paths.
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 22a0389..1812890 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-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt
index 210fde0..6392538 100644
--- a/Documentation/git-symbolic-ref.txt
+++ b/Documentation/git-symbolic-ref.txt
@@ -14,9 +14,9 @@ DESCRIPTION
Given one argument, reads which branch head the given symbolic
ref refers to and outputs its path, relative to the `.git/`
directory. Typically you would give `HEAD` as the <name>
-argument to see on which branch your working tree is on.
+argument to see which branch your working tree is on.
-Give two arguments, create or update a symbolic ref <name> to
+Given two arguments, creates or updates a symbolic ref <name> to
point at the given branch <ref>.
A symbolic ref is a regular file that stores a string that
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index fa73321..5113eae 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -10,14 +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/`
+
+Adds a tag reference in `.git/refs/tags/`.
Unless `-f` is given, the tag must not yet exist in
`.git/refs/tags/` directory.
@@ -85,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
@@ -249,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 c861163..97f7f91 100644
--- a/Documentation/git-verify-pack.txt
+++ b/Documentation/git-verify-pack.txt
@@ -8,7 +8,7 @@ git-verify-pack - Validate packed git archive files
SYNOPSIS
--------
-'git verify-pack' [-v] [--] <pack>.idx ...
+'git verify-pack' [-v|--verbose] [--] <pack>.idx ...
DESCRIPTION
@@ -23,8 +23,15 @@ OPTIONS
The idx files to verify.
-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 26d3850..c8899d5 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 5fd5953..a9bacfb 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]
@@ -327,7 +328,7 @@ Synching repositories
include::cmds-synchingrepositories.txt[]
-The following are helper programs used by the above; end users
+The following are helper commands used by the above; end users
typically do not use them directly.
include::cmds-synchelpers.txt[]
diff --git a/Documentation/gitattributes.txt b/Documentation/gitattributes.txt
index aaa073e..1195e83 100644
--- a/Documentation/gitattributes.txt
+++ b/Documentation/gitattributes.txt
@@ -404,7 +404,7 @@ Performing a three-way merge
The attribute `merge` affects how three versions of a file is
merged when a file-level merge is necessary during `git merge`,
-and other programs such as `git revert` and `git cherry-pick`.
+and other commands such as `git revert` and `git cherry-pick`.
Set::
diff --git a/Documentation/gitcore-tutorial.txt b/Documentation/gitcore-tutorial.txt
index 7ba5e58..b3640c4 100644
--- a/Documentation/gitcore-tutorial.txt
+++ b/Documentation/gitcore-tutorial.txt
@@ -12,7 +12,7 @@ git *
DESCRIPTION
-----------
-This tutorial explains how to use the "core" git programs to set up and
+This tutorial explains how to use the "core" git commands to set up and
work with a git repository.
If you just need to use git as a revision control system you may prefer
@@ -1328,7 +1328,7 @@ into it later. Obviously, this repository creation needs to be
done only once.
[NOTE]
-'git-push' uses a pair of programs,
+'git-push' uses a pair of commands,
'git-send-pack' on your local machine, and 'git-receive-pack'
on the remote machine. The communication between the two over
the network internally uses an SSH connection.
diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt
index 2efe7a4..b26c281 100644
--- a/Documentation/technical/api-run-command.txt
+++ b/Documentation/technical/api-run-command.txt
@@ -35,12 +35,32 @@ Functions
Convenience functions that encapsulate a sequence of
start_command() followed by finish_command(). The argument argv
specifies the program and its arguments. The argument opt is zero
- or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`, or
- `RUN_COMMAND_STDOUT_TO_STDERR` that correspond to the members
- .no_stdin, .git_cmd, .stdout_to_stderr of `struct child_process`.
+ or more of the flags `RUN_COMMAND_NO_STDIN`, `RUN_GIT_CMD`,
+ `RUN_COMMAND_STDOUT_TO_STDERR`, or `RUN_SILENT_EXEC_FAILURE`
+ that correspond to the members .no_stdin, .git_cmd,
+ .stdout_to_stderr, .silent_exec_failure of `struct child_process`.
The argument dir corresponds the member .dir. The argument env
corresponds to the member .env.
+The functions above do the following:
+
+. If a system call failed, errno is set and -1 is returned. A diagnostic
+ is printed.
+
+. If the program was not found, then -1 is returned and errno is set to
+ ENOENT; a diagnostic is printed only if .silent_exec_failure is 0.
+
+. Otherwise, the program is run. If it terminates regularly, its exit
+ code is returned. No diagnistic is printed, even if the exit code is
+ non-zero.
+
+. If the program terminated due to a signal, then the return value is the
+ signal number - 128, ie. it is negative and so indicates an unusual
+ condition; a diagnostic is printed. This return value can be passed to
+ exit(2), which will report the same code to the parent process that a
+ POSIX shell's $? would report for a program that died from the signal.
+
+
`start_async`::
Run a function asynchronously. Takes a pointer to a `struct
@@ -143,6 +163,11 @@ string pointers (NULL terminated) in .env:
To specify a new initial working directory for the sub-process,
specify it in the .dir member.
+If the program cannot be found, the functions return -1 and set
+errno to ENOENT. Normally, an error message is printed, but if
+.silent_exec_failure is set to 1, no message is printed for this
+special error condition.
+
* `struct async`
diff --git a/Documentation/technical/api-tree-walking.txt b/Documentation/technical/api-tree-walking.txt
index e3ddf91..55b7286 100644
--- a/Documentation/technical/api-tree-walking.txt
+++ b/Documentation/technical/api-tree-walking.txt
@@ -1,12 +1,145 @@
tree walking API
================
-Talk about <tree-walk.h>, things like
+The tree walking API is used to traverse and inspect trees.
-* struct tree_desc
-* init_tree_desc
-* tree_entry_extract
-* update_tree_entry
-* get_tree_entry
+Data Structures
+---------------
-(JC, Linus)
+`struct name_entry`::
+
+ An entry in a tree. Each entry has a sha1 identifier, pathname, and
+ mode.
+
+`struct tree_desc`::
+
+ A semi-opaque data structure used to maintain the current state of the
+ walk.
++
+* `buffer` is a pointer into the memory representation of the tree. It always
+points at the current entry being visited.
+
+* `size` counts the number of bytes left in the `buffer`.
+
+* `entry` points to the current entry being visited.
+
+`struct traverse_info`::
+
+ A structure used to maintain the state of a traversal.
++
+* `prev` points to the traverse_info which was used to descend into the
+current tree. If this is the top-level tree `prev` will point to
+a dummy traverse_info.
+
+* `name` is the entry for the current tree (if the tree is a subtree).
+
+* `pathlen` is the length of the full path for the current tree.
+
+* `conflicts` can be used by callbacks to maintain directory-file conflicts.
+
+* `fn` is a callback called for each entry in the tree. See Traversing for more
+information.
+
+* `data` can be anything the `fn` callback would want to use.
+
+Initializing
+------------
+
+`init_tree_desc`::
+
+ Initialize a `tree_desc` and decode its first entry. The buffer and
+ size parameters are assumed to be the same as the buffer and size
+ members of `struct tree`.
+
+`fill_tree_descriptor`::
+
+ Initialize a `tree_desc` and decode its first entry given the sha1 of
+ a tree. Returns the `buffer` member if the sha1 is a valid tree
+ identifier and NULL otherwise.
+
+`setup_traverse_info`::
+
+ Initialize a `traverse_info` given the pathname of the tree to start
+ traversing from. The `base` argument is assumed to be the `path`
+ member of the `name_entry` being recursed into unless the tree is a
+ top-level tree in which case the empty string ("") is used.
+
+Walking
+-------
+
+`tree_entry`::
+
+ Visit the next entry in a tree. Returns 1 when there are more entries
+ left to visit and 0 when all entries have been visited. This is
+ commonly used in the test of a while loop.
+
+`tree_entry_len`::
+
+ Calculate the length of a tree entry's pathname. This utilizes the
+ memory structure of a tree entry to avoid the overhead of using a
+ generic strlen().
+
+`update_tree_entry`::
+
+ Walk to the next entry in a tree. This is commonly used in conjunction
+ with `tree_entry_extract` to inspect the current entry.
+
+`tree_entry_extract`::
+
+ Decode the entry currently being visited (the one pointed to by
+ `tree_desc's` `entry` member) and return the sha1 of the entry. The
+ `pathp` and `modep` arguments are set to the entry's pathname and mode
+ respectively.
+
+`get_tree_entry`::
+
+ Find an entry in a tree given a pathname and the sha1 of a tree to
+ search. Returns 0 if the entry is found and -1 otherwise. The third
+ and fourth parameters are set to the entry's sha1 and mode
+ respectively.
+
+Traversing
+----------
+
+`traverse_trees`::
+
+ Traverse `n` number of trees in parallel. The `fn` callback member of
+ `traverse_info` is called once for each tree entry.
+
+`traverse_callback_t`::
+ The arguments passed to the traverse callback are as follows:
++
+* `n` counts the number of trees being traversed.
+
+* `mask` has its nth bit set if something exists in the nth entry.
+
+* `dirmask` has its nth bit set if the nth tree's entry is a directory.
+
+* `entry` is an array of size `n` where the nth entry is from the nth tree.
+
+* `info` maintains the state of the traversal.
+
++
+Returning a negative value will terminate the traversal. Otherwise the
+return value is treated as an update mask. If the nth bit is set the nth tree
+will be updated and if the bit is not set the nth tree entry will be the
+same in the next callback invocation.
+
+`make_traverse_path`::
+
+ Generate the full pathname of a tree entry based from the root of the
+ traversal. For example, if the traversal has recursed into another
+ tree named "bar" the pathname of an entry "baz" in the "bar"
+ tree would be "bar/baz".
+
+`traverse_path_len`::
+
+ Calculate the length of a pathname returned by `make_traverse_path`.
+ This utilizes the memory structure of a tree entry to avoid the
+ overhead of using a generic strlen().
+
+Authors
+-------
+
+Written by Junio C Hamano <gitster@pobox.com> and Linus Torvalds
+<torvalds@linux-foundation.org>
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index 0b88a51..67ebffa 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -4131,7 +4131,7 @@ What does this mean?
`git rev-list` is the original version of the revision walker, which
_always_ printed a list of revisions to stdout. It is still functional,
-and needs to, since most new Git programs start out as scripts using
+and needs to, since most new Git commands start out as scripts using
`git rev-list`.
`git rev-parse` is not as important any more; it was only used to filter out
diff --git a/Makefile b/Makefile
index daf4296..d842e52 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ all::
# when attempting to read from an fopen'ed directory.
#
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
-# This also implies MOZILLA_SHA1.
+# This also implies BLK_SHA1.
#
# Define NO_CURL if you do not have libcurl installed. git-http-pull and
# git-http-push are not built, and you cannot use http:// and https://
@@ -84,17 +84,13 @@ all::
# specify your own (or DarwinPort's) include directories and
# library directories by defining CFLAGS and LDFLAGS appropriately.
#
+# Define BLK_SHA1 environment variable if you want the C version
+# of the SHA1 that assumes you can do unaligned 32-bit loads and
+# have a fast htonl() function.
+#
# Define PPC_SHA1 environment variable when running make to make use of
# a bundled SHA1 routine optimized for PowerPC.
#
-# Define ARM_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine optimized for ARM.
-#
-# Define MOZILLA_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
-# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
-# choice) has very fast version optimized for i586.
-#
# Define NEEDS_SSL_WITH_CRYPTO if you need -lcrypto with -lssl (Darwin).
#
# Define NEEDS_LIBICONV if linking with libc is not enough (Darwin).
@@ -532,6 +528,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
@@ -621,6 +618,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
@@ -751,9 +749,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
@@ -922,10 +917,6 @@ else
NO_PTHREADS = YesPlease
endif
endif
-ifneq (,$(findstring arm,$(uname_M)))
- ARM_SHA1 = YesPlease
- NO_MKSTEMPS = YesPlease
-endif
-include config.mak.autogen
-include config.mak
@@ -1018,7 +1009,7 @@ ifndef NO_OPENSSL
endif
else
BASIC_CFLAGS += -DNO_OPENSSL
- MOZILLA_SHA1 = 1
+ BLK_SHA1 = 1
OPENSSL_LIBSSL =
endif
ifdef NEEDS_SSL_WITH_CRYPTO
@@ -1167,23 +1158,18 @@ ifdef NO_DEFLATE_BOUND
BASIC_CFLAGS += -DNO_DEFLATE_BOUND
endif
+ifdef BLK_SHA1
+ SHA1_HEADER = "block-sha1/sha1.h"
+ LIB_OBJS += block-sha1/sha1.o
+else
ifdef PPC_SHA1
SHA1_HEADER = "ppc/sha1.h"
LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
else
-ifdef ARM_SHA1
- SHA1_HEADER = "arm/sha1.h"
- LIB_OBJS += arm/sha1.o arm/sha1_arm.o
-else
-ifdef MOZILLA_SHA1
- SHA1_HEADER = "mozilla-sha1/sha1.h"
- LIB_OBJS += mozilla-sha1/sha1.o
-else
SHA1_HEADER = <openssl/sha.h>
EXTLIBS += $(LIB_4_CRYPTO)
endif
endif
-endif
ifdef NO_PERL_MAKEMAKER
export NO_PERL_MAKEMAKER
endif
diff --git a/abspath.c b/abspath.c
index 4bee0ba..b88122c 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/arm/sha1.c b/arm/sha1.c
deleted file mode 100644
index c61ad4a..0000000
--- a/arm/sha1.c
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * SHA-1 implementation optimized for ARM
- *
- * Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org>
- * Created: September 17, 2005
- */
-
-#include <string.h>
-#include "sha1.h"
-
-extern void arm_sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W);
-
-void arm_SHA1_Init(arm_SHA_CTX *c)
-{
- c->len = 0;
- c->hash[0] = 0x67452301;
- c->hash[1] = 0xefcdab89;
- c->hash[2] = 0x98badcfe;
- c->hash[3] = 0x10325476;
- c->hash[4] = 0xc3d2e1f0;
-}
-
-void arm_SHA1_Update(arm_SHA_CTX *c, const void *p, unsigned long n)
-{
- uint32_t workspace[80];
- unsigned int partial;
- unsigned long done;
-
- partial = c->len & 0x3f;
- c->len += n;
- if ((partial + n) >= 64) {
- if (partial) {
- done = 64 - partial;
- memcpy(c->buffer + partial, p, done);
- arm_sha_transform(c->hash, c->buffer, workspace);
- partial = 0;
- } else
- done = 0;
- while (n >= done + 64) {
- arm_sha_transform(c->hash, p + done, workspace);
- done += 64;
- }
- } else
- done = 0;
- if (n - done)
- memcpy(c->buffer + partial, p + done, n - done);
-}
-
-void arm_SHA1_Final(unsigned char *hash, arm_SHA_CTX *c)
-{
- uint64_t bitlen;
- uint32_t bitlen_hi, bitlen_lo;
- unsigned int i, offset, padlen;
- unsigned char bits[8];
- static const unsigned char padding[64] = { 0x80, };
-
- bitlen = c->len << 3;
- offset = c->len & 0x3f;
- padlen = ((offset < 56) ? 56 : (64 + 56)) - offset;
- arm_SHA1_Update(c, padding, padlen);
-
- bitlen_hi = bitlen >> 32;
- bitlen_lo = bitlen & 0xffffffff;
- bits[0] = bitlen_hi >> 24;
- bits[1] = bitlen_hi >> 16;
- bits[2] = bitlen_hi >> 8;
- bits[3] = bitlen_hi;
- bits[4] = bitlen_lo >> 24;
- bits[5] = bitlen_lo >> 16;
- bits[6] = bitlen_lo >> 8;
- bits[7] = bitlen_lo;
- arm_SHA1_Update(c, bits, 8);
-
- for (i = 0; i < 5; i++) {
- uint32_t v = c->hash[i];
- hash[0] = v >> 24;
- hash[1] = v >> 16;
- hash[2] = v >> 8;
- hash[3] = v;
- hash += 4;
- }
-}
diff --git a/arm/sha1.h b/arm/sha1.h
deleted file mode 100644
index b61b618..0000000
--- a/arm/sha1.h
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * SHA-1 implementation optimized for ARM
- *
- * Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org>
- * Created: September 17, 2005
- */
-
-#include <stdint.h>
-
-typedef struct {
- uint64_t len;
- uint32_t hash[5];
- unsigned char buffer[64];
-} arm_SHA_CTX;
-
-void arm_SHA1_Init(arm_SHA_CTX *c);
-void arm_SHA1_Update(arm_SHA_CTX *c, const void *p, unsigned long n);
-void arm_SHA1_Final(unsigned char *hash, arm_SHA_CTX *c);
-
-#define git_SHA_CTX arm_SHA_CTX
-#define git_SHA1_Init arm_SHA1_Init
-#define git_SHA1_Update arm_SHA1_Update
-#define git_SHA1_Final arm_SHA1_Final
diff --git a/arm/sha1_arm.S b/arm/sha1_arm.S
deleted file mode 100644
index 41e9263..0000000
--- a/arm/sha1_arm.S
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * SHA transform optimized for ARM
- *
- * Copyright: (C) 2005 by Nicolas Pitre <nico@cam.org>
- * Created: September 17, 2005
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-
- .text
- .globl arm_sha_transform
-
-/*
- * void sha_transform(uint32_t *hash, const unsigned char *data, uint32_t *W);
- *
- * note: the "data" pointer may be unaligned.
- */
-
-arm_sha_transform:
-
- stmfd sp!, {r4 - r8, lr}
-
- @ for (i = 0; i < 16; i++)
- @ W[i] = ntohl(((uint32_t *)data)[i]);
-
-#ifdef __ARMEB__
- mov r4, r0
- mov r0, r2
- mov r2, #64
- bl memcpy
- mov r2, r0
- mov r0, r4
-#else
- mov r3, r2
- mov lr, #16
-1: ldrb r4, [r1], #1
- ldrb r5, [r1], #1
- ldrb r6, [r1], #1
- ldrb r7, [r1], #1
- subs lr, lr, #1
- orr r5, r5, r4, lsl #8
- orr r6, r6, r5, lsl #8
- orr r7, r7, r6, lsl #8
- str r7, [r3], #4
- bne 1b
-#endif
-
- @ for (i = 0; i < 64; i++)
- @ W[i+16] = ror(W[i+13] ^ W[i+8] ^ W[i+2] ^ W[i], 31);
-
- sub r3, r2, #4
- mov lr, #64
-2: ldr r4, [r3, #4]!
- subs lr, lr, #1
- ldr r5, [r3, #8]
- ldr r6, [r3, #32]
- ldr r7, [r3, #52]
- eor r4, r4, r5
- eor r4, r4, r6
- eor r4, r4, r7
- mov r4, r4, ror #31
- str r4, [r3, #64]
- bne 2b
-
- /*
- * The SHA functions are:
- *
- * f1(B,C,D) = (D ^ (B & (C ^ D)))
- * f2(B,C,D) = (B ^ C ^ D)
- * f3(B,C,D) = ((B & C) | (D & (B | C)))
- *
- * Then the sub-blocks are processed as follows:
- *
- * A' = ror(A, 27) + f(B,C,D) + E + K + *W++
- * B' = A
- * C' = ror(B, 2)
- * D' = C
- * E' = D
- *
- * We therefore unroll each loop 5 times to avoid register shuffling.
- * Also the ror for C (and also D and E which are successivelyderived
- * from it) is applied in place to cut on an additional mov insn for
- * each round.
- */
-
- .macro sha_f1, A, B, C, D, E
- ldr r3, [r2], #4
- eor ip, \C, \D
- add \E, r1, \E, ror #2
- and ip, \B, ip, ror #2
- add \E, \E, \A, ror #27
- eor ip, ip, \D, ror #2
- add \E, \E, r3
- add \E, \E, ip
- .endm
-
- .macro sha_f2, A, B, C, D, E
- ldr r3, [r2], #4
- add \E, r1, \E, ror #2
- eor ip, \B, \C, ror #2
- add \E, \E, \A, ror #27
- eor ip, ip, \D, ror #2
- add \E, \E, r3
- add \E, \E, ip
- .endm
-
- .macro sha_f3, A, B, C, D, E
- ldr r3, [r2], #4
- add \E, r1, \E, ror #2
- orr ip, \B, \C, ror #2
- add \E, \E, \A, ror #27
- and ip, ip, \D, ror #2
- add \E, \E, r3
- and r3, \B, \C, ror #2
- orr ip, ip, r3
- add \E, \E, ip
- .endm
-
- ldmia r0, {r4 - r8}
-
- mov lr, #4
- ldr r1, .L_sha_K + 0
-
- /* adjust initial values */
- mov r6, r6, ror #30
- mov r7, r7, ror #30
- mov r8, r8, ror #30
-
-3: subs lr, lr, #1
- sha_f1 r4, r5, r6, r7, r8
- sha_f1 r8, r4, r5, r6, r7
- sha_f1 r7, r8, r4, r5, r6
- sha_f1 r6, r7, r8, r4, r5
- sha_f1 r5, r6, r7, r8, r4
- bne 3b
-
- ldr r1, .L_sha_K + 4
- mov lr, #4
-
-4: subs lr, lr, #1
- sha_f2 r4, r5, r6, r7, r8
- sha_f2 r8, r4, r5, r6, r7
- sha_f2 r7, r8, r4, r5, r6
- sha_f2 r6, r7, r8, r4, r5
- sha_f2 r5, r6, r7, r8, r4
- bne 4b
-
- ldr r1, .L_sha_K + 8
- mov lr, #4
-
-5: subs lr, lr, #1
- sha_f3 r4, r5, r6, r7, r8
- sha_f3 r8, r4, r5, r6, r7
- sha_f3 r7, r8, r4, r5, r6
- sha_f3 r6, r7, r8, r4, r5
- sha_f3 r5, r6, r7, r8, r4
- bne 5b
-
- ldr r1, .L_sha_K + 12
- mov lr, #4
-
-6: subs lr, lr, #1
- sha_f2 r4, r5, r6, r7, r8
- sha_f2 r8, r4, r5, r6, r7
- sha_f2 r7, r8, r4, r5, r6
- sha_f2 r6, r7, r8, r4, r5
- sha_f2 r5, r6, r7, r8, r4
- bne 6b
-
- ldmia r0, {r1, r2, r3, ip, lr}
- add r4, r1, r4
- add r5, r2, r5
- add r6, r3, r6, ror #2
- add r7, ip, r7, ror #2
- add r8, lr, r8, ror #2
- stmia r0, {r4 - r8}
-
- ldmfd sp!, {r4 - r8, pc}
-
-.L_sha_K:
- .word 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xca62c1d6
diff --git a/bisect.c b/bisect.c
index 7f20acb..dc18db8 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/block-sha1/sha1.c b/block-sha1/sha1.c
new file mode 100644
index 0000000..d893475
--- /dev/null
+++ b/block-sha1/sha1.c
@@ -0,0 +1,282 @@
+/*
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
+ */
+
+/* this is only to get definitions for memcpy(), ntohl() and htonl() */
+#include "../git-compat-util.h"
+
+#include "sha1.h"
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+/*
+ * Force usage of rol or ror by selecting the one with the smaller constant.
+ * It _can_ generate slightly smaller code (a constant of 1 is special), but
+ * perhaps more importantly it's possibly faster on any uarch that does a
+ * rotate with a loop.
+ */
+
+#define SHA_ASM(op, x, n) ({ unsigned int __res; __asm__(op " %1,%0":"=r" (__res):"i" (n), "0" (x)); __res; })
+#define SHA_ROL(x,n) SHA_ASM("rol", x, n)
+#define SHA_ROR(x,n) SHA_ASM("ror", x, n)
+
+#else
+
+#define SHA_ROT(X,l,r) (((X) << (l)) | ((X) >> (r)))
+#define SHA_ROL(X,n) SHA_ROT(X,n,32-(n))
+#define SHA_ROR(X,n) SHA_ROT(X,32-(n),n)
+
+#endif
+
+/*
+ * If you have 32 registers or more, the compiler can (and should)
+ * try to change the array[] accesses into registers. However, on
+ * machines with less than ~25 registers, that won't really work,
+ * and at least gcc will make an unholy mess of it.
+ *
+ * So to avoid that mess which just slows things down, we force
+ * the stores to memory to actually happen (we might be better off
+ * with a 'W(t)=(val);asm("":"+m" (W(t))' there instead, as
+ * suggested by Artur Skawina - that will also make gcc unable to
+ * try to do the silly "optimize away loads" part because it won't
+ * see what the value will be).
+ *
+ * Ben Herrenschmidt reports that on PPC, the C version comes close
+ * to the optimized asm with this (ie on PPC you don't want that
+ * 'volatile', since there are lots of registers).
+ *
+ * On ARM we get the best code generation by forcing a full memory barrier
+ * between each SHA_ROUND, otherwise gcc happily get wild with spilling and
+ * the stack frame size simply explode and performance goes down the drain.
+ */
+
+#if defined(__i386__) || defined(__x86_64__)
+ #define setW(x, val) (*(volatile unsigned int *)&W(x) = (val))
+#elif defined(__GNUC__) && defined(__arm__)
+ #define setW(x, val) do { W(x) = (val); __asm__("":::"memory"); } while (0)
+#else
+ #define setW(x, val) (W(x) = (val))
+#endif
+
+/*
+ * Performance might be improved if the CPU architecture is OK with
+ * unaligned 32-bit loads and a fast ntohl() is available.
+ * Otherwise fall back to byte loads and shifts which is portable,
+ * and is faster on architectures with memory alignment issues.
+ */
+
+#if defined(__i386__) || defined(__x86_64__) || \
+ defined(__ppc__) || defined(__ppc64__) || \
+ defined(__powerpc__) || defined(__powerpc64__) || \
+ defined(__s390__) || defined(__s390x__)
+
+#define get_be32(p) ntohl(*(unsigned int *)(p))
+#define put_be32(p, v) do { *(unsigned int *)(p) = htonl(v); } while (0)
+
+#else
+
+#define get_be32(p) ( \
+ (*((unsigned char *)(p) + 0) << 24) | \
+ (*((unsigned char *)(p) + 1) << 16) | \
+ (*((unsigned char *)(p) + 2) << 8) | \
+ (*((unsigned char *)(p) + 3) << 0) )
+#define put_be32(p, v) do { \
+ unsigned int __v = (v); \
+ *((unsigned char *)(p) + 0) = __v >> 24; \
+ *((unsigned char *)(p) + 1) = __v >> 16; \
+ *((unsigned char *)(p) + 2) = __v >> 8; \
+ *((unsigned char *)(p) + 3) = __v >> 0; } while (0)
+
+#endif
+
+/* This "rolls" over the 512-bit array */
+#define W(x) (array[(x)&15])
+
+/*
+ * Where do we get the source from? The first 16 iterations get it from
+ * the input data, the next mix it from the 512-bit array.
+ */
+#define SHA_SRC(t) get_be32(data + t)
+#define SHA_MIX(t) SHA_ROL(W(t+13) ^ W(t+8) ^ W(t+2) ^ W(t), 1)
+
+#define SHA_ROUND(t, input, fn, constant, A, B, C, D, E) do { \
+ unsigned int TEMP = input(t); setW(t, TEMP); \
+ E += TEMP + SHA_ROL(A,5) + (fn) + (constant); \
+ B = SHA_ROR(B, 2); } while (0)
+
+#define T_0_15(t, A, B, C, D, E) SHA_ROUND(t, SHA_SRC, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_16_19(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (((C^D)&B)^D) , 0x5a827999, A, B, C, D, E )
+#define T_20_39(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0x6ed9eba1, A, B, C, D, E )
+#define T_40_59(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, ((B&C)+(D&(B^C))) , 0x8f1bbcdc, A, B, C, D, E )
+#define T_60_79(t, A, B, C, D, E) SHA_ROUND(t, SHA_MIX, (B^C^D) , 0xca62c1d6, A, B, C, D, E )
+
+static void blk_SHA1_Block(blk_SHA_CTX *ctx, const unsigned int *data)
+{
+ unsigned int A,B,C,D,E;
+ unsigned int array[16];
+
+ A = ctx->H[0];
+ B = ctx->H[1];
+ C = ctx->H[2];
+ D = ctx->H[3];
+ E = ctx->H[4];
+
+ /* Round 1 - iterations 0-16 take their input from 'data' */
+ T_0_15( 0, A, B, C, D, E);
+ T_0_15( 1, E, A, B, C, D);
+ T_0_15( 2, D, E, A, B, C);
+ T_0_15( 3, C, D, E, A, B);
+ T_0_15( 4, B, C, D, E, A);
+ T_0_15( 5, A, B, C, D, E);
+ T_0_15( 6, E, A, B, C, D);
+ T_0_15( 7, D, E, A, B, C);
+ T_0_15( 8, C, D, E, A, B);
+ T_0_15( 9, B, C, D, E, A);
+ T_0_15(10, A, B, C, D, E);
+ T_0_15(11, E, A, B, C, D);
+ T_0_15(12, D, E, A, B, C);
+ T_0_15(13, C, D, E, A, B);
+ T_0_15(14, B, C, D, E, A);
+ T_0_15(15, A, B, C, D, E);
+
+ /* Round 1 - tail. Input from 512-bit mixing array */
+ T_16_19(16, E, A, B, C, D);
+ T_16_19(17, D, E, A, B, C);
+ T_16_19(18, C, D, E, A, B);
+ T_16_19(19, B, C, D, E, A);
+
+ /* Round 2 */
+ T_20_39(20, A, B, C, D, E);
+ T_20_39(21, E, A, B, C, D);
+ T_20_39(22, D, E, A, B, C);
+ T_20_39(23, C, D, E, A, B);
+ T_20_39(24, B, C, D, E, A);
+ T_20_39(25, A, B, C, D, E);
+ T_20_39(26, E, A, B, C, D);
+ T_20_39(27, D, E, A, B, C);
+ T_20_39(28, C, D, E, A, B);
+ T_20_39(29, B, C, D, E, A);
+ T_20_39(30, A, B, C, D, E);
+ T_20_39(31, E, A, B, C, D);
+ T_20_39(32, D, E, A, B, C);
+ T_20_39(33, C, D, E, A, B);
+ T_20_39(34, B, C, D, E, A);
+ T_20_39(35, A, B, C, D, E);
+ T_20_39(36, E, A, B, C, D);
+ T_20_39(37, D, E, A, B, C);
+ T_20_39(38, C, D, E, A, B);
+ T_20_39(39, B, C, D, E, A);
+
+ /* Round 3 */
+ T_40_59(40, A, B, C, D, E);
+ T_40_59(41, E, A, B, C, D);
+ T_40_59(42, D, E, A, B, C);
+ T_40_59(43, C, D, E, A, B);
+ T_40_59(44, B, C, D, E, A);
+ T_40_59(45, A, B, C, D, E);
+ T_40_59(46, E, A, B, C, D);
+ T_40_59(47, D, E, A, B, C);
+ T_40_59(48, C, D, E, A, B);
+ T_40_59(49, B, C, D, E, A);
+ T_40_59(50, A, B, C, D, E);
+ T_40_59(51, E, A, B, C, D);
+ T_40_59(52, D, E, A, B, C);
+ T_40_59(53, C, D, E, A, B);
+ T_40_59(54, B, C, D, E, A);
+ T_40_59(55, A, B, C, D, E);
+ T_40_59(56, E, A, B, C, D);
+ T_40_59(57, D, E, A, B, C);
+ T_40_59(58, C, D, E, A, B);
+ T_40_59(59, B, C, D, E, A);
+
+ /* Round 4 */
+ T_60_79(60, A, B, C, D, E);
+ T_60_79(61, E, A, B, C, D);
+ T_60_79(62, D, E, A, B, C);
+ T_60_79(63, C, D, E, A, B);
+ T_60_79(64, B, C, D, E, A);
+ T_60_79(65, A, B, C, D, E);
+ T_60_79(66, E, A, B, C, D);
+ T_60_79(67, D, E, A, B, C);
+ T_60_79(68, C, D, E, A, B);
+ T_60_79(69, B, C, D, E, A);
+ T_60_79(70, A, B, C, D, E);
+ T_60_79(71, E, A, B, C, D);
+ T_60_79(72, D, E, A, B, C);
+ T_60_79(73, C, D, E, A, B);
+ T_60_79(74, B, C, D, E, A);
+ T_60_79(75, A, B, C, D, E);
+ T_60_79(76, E, A, B, C, D);
+ T_60_79(77, D, E, A, B, C);
+ T_60_79(78, C, D, E, A, B);
+ T_60_79(79, B, C, D, E, A);
+
+ ctx->H[0] += A;
+ ctx->H[1] += B;
+ ctx->H[2] += C;
+ ctx->H[3] += D;
+ ctx->H[4] += E;
+}
+
+void blk_SHA1_Init(blk_SHA_CTX *ctx)
+{
+ ctx->size = 0;
+
+ /* Initialize H with the magic constants (see FIPS180 for constants) */
+ ctx->H[0] = 0x67452301;
+ ctx->H[1] = 0xefcdab89;
+ ctx->H[2] = 0x98badcfe;
+ ctx->H[3] = 0x10325476;
+ ctx->H[4] = 0xc3d2e1f0;
+}
+
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *data, unsigned long len)
+{
+ int lenW = ctx->size & 63;
+
+ ctx->size += len;
+
+ /* Read the data into W and process blocks as they get full */
+ if (lenW) {
+ int left = 64 - lenW;
+ if (len < left)
+ left = len;
+ memcpy(lenW + (char *)ctx->W, data, left);
+ lenW = (lenW + left) & 63;
+ len -= left;
+ data = ((const char *)data + left);
+ if (lenW)
+ return;
+ blk_SHA1_Block(ctx, ctx->W);
+ }
+ while (len >= 64) {
+ blk_SHA1_Block(ctx, data);
+ data = ((const char *)data + 64);
+ len -= 64;
+ }
+ if (len)
+ memcpy(ctx->W, data, len);
+}
+
+void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx)
+{
+ static const unsigned char pad[64] = { 0x80 };
+ unsigned int padlen[2];
+ int i;
+
+ /* Pad with a binary 1 (ie 0x80), then zeroes, then length */
+ padlen[0] = htonl(ctx->size >> 29);
+ padlen[1] = htonl(ctx->size << 3);
+
+ i = ctx->size & 63;
+ blk_SHA1_Update(ctx, pad, 1+ (63 & (55 - i)));
+ blk_SHA1_Update(ctx, padlen, 8);
+
+ /* Output hash */
+ for (i = 0; i < 5; i++)
+ put_be32(hashout + i*4, ctx->H[i]);
+}
diff --git a/block-sha1/sha1.h b/block-sha1/sha1.h
new file mode 100644
index 0000000..b864df6
--- /dev/null
+++ b/block-sha1/sha1.h
@@ -0,0 +1,22 @@
+/*
+ * SHA1 routine optimized to do word accesses rather than byte accesses,
+ * and to avoid unnecessary copies into the context array.
+ *
+ * This was initially based on the Mozilla SHA1 implementation, although
+ * none of the original Mozilla code remains.
+ */
+
+typedef struct {
+ unsigned long long size;
+ unsigned int H[5];
+ unsigned int W[16];
+} blk_SHA_CTX;
+
+void blk_SHA1_Init(blk_SHA_CTX *ctx);
+void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len);
+void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
+
+#define git_SHA_CTX blk_SHA_CTX
+#define git_SHA1_Init blk_SHA1_Init
+#define git_SHA1_Update blk_SHA1_Update
+#define git_SHA1_Final blk_SHA1_Final
diff --git a/builtin-apply.c b/builtin-apply.c
index dc0ff5e..ae11b41 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);
@@ -458,6 +536,76 @@ static int guess_p_value(const char *nameline)
}
/*
+ * Does the ---/+++ line has the POSIX timestamp after the last HT?
+ * GNU diff puts epoch there to signal a creation/deletion event. Is
+ * this such a timestamp?
+ */
+static int has_epoch_timestamp(const char *nameline)
+{
+ /*
+ * We are only interested in epoch timestamp; any non-zero
+ * fraction cannot be one, hence "(\.0+)?" in the regexp below.
+ * For the same reason, the date must be either 1969-12-31 or
+ * 1970-01-01, and the seconds part must be "00".
+ */
+ const char stamp_regexp[] =
+ "^(1969-12-31|1970-01-01)"
+ " "
+ "[0-2][0-9]:[0-5][0-9]:00(\\.0+)?"
+ " "
+ "([-+][0-2][0-9][0-5][0-9])\n";
+ const char *timestamp = NULL, *cp;
+ static regex_t *stamp;
+ regmatch_t m[10];
+ int zoneoffset;
+ int hourminute;
+ int status;
+
+ for (cp = nameline; *cp != '\n'; cp++) {
+ if (*cp == '\t')
+ timestamp = cp + 1;
+ }
+ if (!timestamp)
+ return 0;
+ if (!stamp) {
+ stamp = xmalloc(sizeof(*stamp));
+ if (regcomp(stamp, stamp_regexp, REG_EXTENDED)) {
+ warning("Cannot prepare timestamp regexp %s",
+ stamp_regexp);
+ return 0;
+ }
+ }
+
+ status = regexec(stamp, timestamp, ARRAY_SIZE(m), m, 0);
+ if (status) {
+ if (status != REG_NOMATCH)
+ warning("regexec returned %d for input: %s",
+ status, timestamp);
+ return 0;
+ }
+
+ zoneoffset = strtol(timestamp + m[3].rm_so + 1, NULL, 10);
+ zoneoffset = (zoneoffset / 100) * 60 + (zoneoffset % 100);
+ if (timestamp[m[3].rm_so] == '-')
+ zoneoffset = -zoneoffset;
+
+ /*
+ * YYYY-MM-DD hh:mm:ss must be from either 1969-12-31
+ * (west of GMT) or 1970-01-01 (east of GMT)
+ */
+ if ((zoneoffset < 0 && memcmp(timestamp, "1969-12-31", 10)) ||
+ (0 <= zoneoffset && memcmp(timestamp, "1970-01-01", 10)))
+ return 0;
+
+ hourminute = (strtol(timestamp + 11, NULL, 10) * 60 +
+ strtol(timestamp + 14, NULL, 10) -
+ zoneoffset);
+
+ return ((zoneoffset < 0 && hourminute == 1440) ||
+ (0 <= zoneoffset && !hourminute));
+}
+
+/*
* Get the name etc info from the ---/+++ lines of a traditional patch header
*
* FIXME! The end-of-filename heuristics are kind of screwy. For existing
@@ -493,7 +641,17 @@ static void parse_traditional_patch(const char *first, const char *second, struc
} else {
name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
name = find_name(second, name, p_value, TERM_SPACE | TERM_TAB);
- patch->old_name = patch->new_name = name;
+ if (has_epoch_timestamp(first)) {
+ patch->is_new = 1;
+ patch->is_delete = 0;
+ patch->new_name = name;
+ } else if (has_epoch_timestamp(second)) {
+ patch->is_new = 0;
+ patch->is_delete = 1;
+ patch->old_name = name;
+ } else {
+ patch->old_name = patch->new_name = name;
+ }
}
if (!name)
die("unable to find filename in patch at line %d", linenr);
@@ -1592,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;
@@ -1614,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;
@@ -1693,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;
@@ -1750,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:
@@ -3192,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);
}
@@ -3228,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)
{
@@ -3304,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,
@@ -3328,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-clean.c b/builtin-clean.c
index 2d8c735..05c763c 100644
--- a/builtin-clean.c
+++ b/builtin-clean.c
@@ -31,6 +31,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
int i;
int show_only = 0, remove_directories = 0, quiet = 0, ignored = 0;
int ignored_only = 0, baselen = 0, config_set = 0, errors = 0;
+ int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
struct strbuf directory = STRBUF_INIT;
struct dir_struct dir;
static const char **pathspec;
@@ -69,6 +70,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
die("clean.requireForce%s set and -n or -f not given; "
"refusing to clean", config_set ? "" : " not");
+ if (force > 1)
+ rm_flags = 0;
+
dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
if (!ignored)
@@ -131,7 +135,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
(matches == MATCHED_EXACTLY)) {
if (!quiet)
printf("Removing %s\n", qname);
- if (remove_dir_recursively(&directory, 0) != 0) {
+ if (remove_dir_recursively(&directory,
+ rm_flags) != 0) {
warning("failed to remove '%s'", qname);
errors++;
}
diff --git a/builtin-clone.c b/builtin-clone.c
index 32dea74..0d2b4a8 100644
--- a/builtin-clone.c
+++ b/builtin-clone.c
@@ -38,7 +38,7 @@ static const char * const builtin_clone_usage[] = {
};
static int option_quiet, option_no_checkout, option_bare, option_mirror;
-static int option_local, option_no_hardlinks, option_shared;
+static int option_local, option_no_hardlinks, option_shared, option_recursive;
static char *option_template, *option_reference, *option_depth;
static char *option_origin = NULL;
static char *option_upload_pack = "git-upload-pack";
@@ -59,6 +59,8 @@ static struct option builtin_clone_options[] = {
"don't use local hardlinks, always copy"),
OPT_BOOLEAN('s', "shared", &option_shared,
"setup as shared repository"),
+ OPT_BOOLEAN(0, "recursive", &option_recursive,
+ "setup as shared repository"),
OPT_STRING(0, "template", &option_template, "path",
"path the template repository"),
OPT_STRING(0, "reference", &option_reference, "repo",
@@ -73,6 +75,10 @@ static struct option builtin_clone_options[] = {
OPT_END()
};
+static const char *argv_submodule[] = {
+ "submodule", "update", "--init", "--recursive", NULL
+};
+
static char *get_repo_path(const char *repo, int *is_bundle)
{
static char *suffix[] = { "/.git", ".git", "" };
@@ -608,6 +614,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
err |= run_hook(NULL, "post-checkout", sha1_to_hex(null_sha1),
sha1_to_hex(remote_head->old_sha1), "1", NULL);
+
+ if (!err && option_recursive)
+ err = run_command_v_opt(argv_submodule, RUN_GIT_CMD);
}
strbuf_release(&reflog_msg);
diff --git a/builtin-describe.c b/builtin-describe.c
index 7a66298..df67a73 100644
--- a/builtin-describe.c
+++ b/builtin-describe.c
@@ -20,6 +20,7 @@ static int tags; /* Allow lightweight tags */
static int longformat;
static int abbrev = DEFAULT_ABBREV;
static int max_candidates = 10;
+static int found_names;
static const char *pattern;
static int always;
@@ -49,6 +50,7 @@ static void add_to_known_names(const char *path,
memcpy(e->path, path, len);
commit->util = e;
}
+ found_names = 1;
}
static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
@@ -195,6 +197,9 @@ static void describe(const char *arg, int last_one)
for_each_ref(get_name, NULL);
}
+ if (!found_names)
+ die("cannot describe '%s'", sha1_to_hex(sha1));
+
n = cmit->util;
if (n) {
/*
diff --git a/builtin-diff.c b/builtin-diff.c
index 2e51f40..ffcdd05 100644
--- a/builtin-diff.c
+++ b/builtin-diff.c
@@ -218,6 +218,8 @@ static int builtin_diff_files(struct rev_info *revs, int argc, const char **argv
revs->max_count = 3;
else if (!strcmp(argv[1], "-q"))
options |= DIFF_SILENT_ON_REMOVED;
+ else if (!strcmp(argv[1], "-h"))
+ usage(builtin_diff_usage);
else
return error("invalid option: %s", argv[1]);
argv++; argc--;
diff --git a/builtin-fsck.c b/builtin-fsck.c
index b3d38fa..c58b0e3 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-grep.c b/builtin-grep.c
index f477659..ad0e0a5 100644
--- a/builtin-grep.c
+++ b/builtin-grep.c
@@ -53,25 +53,57 @@ static int grep_config(const char *var, const char *value, void *cb)
}
/*
+ * Return non-zero if max_depth is negative or path has no more then max_depth
+ * slashes.
+ */
+static int accept_subdir(const char *path, int max_depth)
+{
+ if (max_depth < 0)
+ return 1;
+
+ while ((path = strchr(path, '/')) != NULL) {
+ max_depth--;
+ if (max_depth < 0)
+ return 0;
+ path++;
+ }
+ return 1;
+}
+
+/*
+ * Return non-zero if name is a subdirectory of match and is not too deep.
+ */
+static int is_subdir(const char *name, int namelen,
+ const char *match, int matchlen, int max_depth)
+{
+ if (matchlen > namelen || strncmp(name, match, matchlen))
+ return 0;
+
+ if (name[matchlen] == '\0') /* exact match */
+ return 1;
+
+ if (!matchlen || match[matchlen-1] == '/' || name[matchlen] == '/')
+ return accept_subdir(name + matchlen + 1, max_depth);
+
+ return 0;
+}
+
+/*
* git grep pathspecs are somewhat different from diff-tree pathspecs;
* pathname wildcards are allowed.
*/
-static int pathspec_matches(const char **paths, const char *name)
+static int pathspec_matches(const char **paths, const char *name, int max_depth)
{
int namelen, i;
if (!paths || !*paths)
- return 1;
+ return accept_subdir(name, max_depth);
namelen = strlen(name);
for (i = 0; paths[i]; i++) {
const char *match = paths[i];
int matchlen = strlen(match);
const char *cp, *meta;
- if (!matchlen ||
- ((matchlen <= namelen) &&
- !strncmp(name, match, matchlen) &&
- (match[matchlen-1] == '/' ||
- name[matchlen] == '\0' || name[matchlen] == '/')))
+ if (is_subdir(name, namelen, match, matchlen, max_depth))
return 1;
if (!fnmatch(match, name, 0))
return 1;
@@ -421,7 +453,7 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
int kept;
if (!S_ISREG(ce->ce_mode))
continue;
- if (!pathspec_matches(paths, ce->name))
+ if (!pathspec_matches(paths, ce->name, opt->max_depth))
continue;
name = ce->name;
if (name[0] == '-') {
@@ -478,7 +510,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
struct cache_entry *ce = active_cache[nr];
if (!S_ISREG(ce->ce_mode))
continue;
- if (!pathspec_matches(paths, ce->name))
+ if (!pathspec_matches(paths, ce->name, opt->max_depth))
continue;
/*
* If CE_VALID is on, we assume worktree file and its cache entry
@@ -538,7 +570,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
strbuf_addch(&pathbuf, '/');
down = pathbuf.buf + tn_len;
- if (!pathspec_matches(paths, down))
+ if (!pathspec_matches(paths, down, opt->max_depth))
;
else if (S_ISREG(entry.mode))
hit |= grep_sha1(opt, entry.sha1, pathbuf.buf, tn_len);
@@ -692,6 +724,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
OPT_SET_INT('I', NULL, &opt.binary,
"don't match patterns in binary files",
GREP_BINARY_NOMATCH),
+ { OPTION_INTEGER, 0, "max-depth", &opt.max_depth, "depth",
+ "descend at most <depth> levels", PARSE_OPT_NONEG,
+ NULL, 1 },
OPT_GROUP(""),
OPT_BIT('E', "extended-regexp", &opt.regflags,
"use extended POSIX regular expressions", REG_EXTENDED),
@@ -768,6 +803,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
opt.pathname = 1;
opt.pattern_tail = &opt.pattern_list;
opt.regflags = REG_NEWLINE;
+ opt.max_depth = -1;
strcpy(opt.color_match, GIT_COLOR_RED GIT_COLOR_BOLD);
opt.color = -1;
diff --git a/builtin-init-db.c b/builtin-init-db.c
index 4a56006..dd84cae 100644
--- a/builtin-init-db.c
+++ b/builtin-init-db.c
@@ -6,6 +6,7 @@
#include "cache.h"
#include "builtin.h"
#include "exec_cmd.h"
+#include "parse-options.h"
#ifndef DEFAULT_GIT_TEMPLATE_DIR
#define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
@@ -370,8 +371,16 @@ static int guess_repository_type(const char *git_dir)
return 1;
}
-static const char init_db_usage[] =
-"git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]]";
+static int shared_callback(const struct option *opt, const char *arg, int unset)
+{
+ *((int *) opt->value) = (arg) ? git_config_perm("arg", arg) : PERM_GROUP;
+ return 0;
+}
+
+static const char *const init_db_usage[] = {
+ "git init [-q | --quiet] [--bare] [--template=<template-directory>] [--shared[=<permissions>]] [directory]",
+ NULL
+};
/*
* If you want to, you can share the DB area with any number of branches.
@@ -384,25 +393,60 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
const char *git_dir;
const char *template_dir = NULL;
unsigned int flags = 0;
- int i;
-
- for (i = 1; i < argc; i++, argv++) {
- const char *arg = argv[1];
- if (!prefixcmp(arg, "--template="))
- template_dir = arg+11;
- else if (!strcmp(arg, "--bare")) {
- static char git_dir[PATH_MAX+1];
- is_bare_repository_cfg = 1;
- setenv(GIT_DIR_ENVIRONMENT, getcwd(git_dir,
- sizeof(git_dir)), 0);
- } else if (!strcmp(arg, "--shared"))
- init_shared_repository = PERM_GROUP;
- else if (!prefixcmp(arg, "--shared="))
- init_shared_repository = git_config_perm("arg", arg+9);
- else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet"))
- flags |= INIT_DB_QUIET;
- else
- usage(init_db_usage);
+ const struct option init_db_options[] = {
+ OPT_STRING(0, "template", &template_dir, "template-directory",
+ "provide the directory from which templates will be used"),
+ OPT_SET_INT(0, "bare", &is_bare_repository_cfg,
+ "create a bare repository", 1),
+ { OPTION_CALLBACK, 0, "shared", &init_shared_repository,
+ "permissions",
+ "specify that the git repository is to be shared amongst several users",
+ PARSE_OPT_OPTARG | PARSE_OPT_NONEG, shared_callback, 0},
+ OPT_BIT('q', "quiet", &flags, "be quiet", INIT_DB_QUIET),
+ OPT_END()
+ };
+
+ argc = parse_options(argc, argv, prefix, init_db_options, init_db_usage, 0);
+
+ if (argc == 1) {
+ int mkdir_tried = 0;
+ retry:
+ if (chdir(argv[0]) < 0) {
+ if (!mkdir_tried) {
+ int saved;
+ /*
+ * At this point we haven't read any configuration,
+ * and we know shared_repository should always be 0;
+ * but just in case we play safe.
+ */
+ saved = shared_repository;
+ shared_repository = 0;
+ switch (safe_create_leading_directories_const(argv[0])) {
+ case -3:
+ errno = EEXIST;
+ /* fallthru */
+ case -1:
+ die_errno("cannot mkdir %s", argv[0]);
+ break;
+ default:
+ break;
+ }
+ shared_repository = saved;
+ if (mkdir(argv[0], 0777) < 0)
+ die_errno("cannot mkdir %s", argv[0]);
+ mkdir_tried = 1;
+ goto retry;
+ }
+ die_errno("cannot chdir to %s", argv[0]);
+ }
+ } else if (0 < argc) {
+ usage(init_db_usage[0]);
+ }
+ if (is_bare_repository_cfg == 1) {
+ static char git_dir[PATH_MAX+1];
+
+ setenv(GIT_DIR_ENVIRONMENT,
+ getcwd(git_dir, sizeof(git_dir)), 0);
}
if (init_shared_repository != -1)
diff --git a/builtin-log.c b/builtin-log.c
index 0c2fa0a..25e21ed 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -27,10 +27,15 @@ static int default_show_root = 1;
static const char *fmt_patch_subject_prefix = "PATCH";
static const char *fmt_pretty;
+static const char * const builtin_log_usage =
+ "git log [<options>] [<since>..<until>] [[--] <path>...]\n"
+ " or: git show [options] <object>...";
+
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;
@@ -57,13 +62,26 @@ 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")) {
+ usage(builtin_log_usage);
} else
die("unrecognized argument: %s", arg);
}
+ if (decoration_style) {
+ rev->show_decorations = 1;
+ load_ref_decorations(decoration_style);
+ }
}
/*
@@ -257,7 +275,7 @@ static void show_tagger(char *buf, int len, struct rev_info *rev)
pp_user_info("Tagger", rev->commit_format, &out, buf, rev->date_mode,
git_log_output_encoding ?
git_log_output_encoding: git_commit_encoding);
- printf("%s\n", out.buf);
+ printf("%s", out.buf);
strbuf_release(&out);
}
@@ -329,11 +347,14 @@ int cmd_show(int argc, const char **argv, const char *prefix)
case OBJ_TAG: {
struct tag *t = (struct tag *)o;
+ if (rev.shown_one)
+ putchar('\n');
printf("%stag %s%s\n",
diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
t->tag,
diff_get_color_opt(&rev.diffopt, DIFF_RESET));
ret = show_object(o->sha1, 1, &rev);
+ rev.shown_one = 1;
if (ret)
break;
o = parse_object(t->tagged->sha1);
@@ -345,12 +366,15 @@ int cmd_show(int argc, const char **argv, const char *prefix)
break;
}
case OBJ_TREE:
+ if (rev.shown_one)
+ putchar('\n');
printf("%stree %s%s\n\n",
diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
name,
diff_get_color_opt(&rev.diffopt, DIFF_RESET));
read_tree_recursive((struct tree *)o, "", 0, 0, NULL,
show_tree_object, NULL);
+ rev.shown_one = 1;
break;
case OBJ_COMMIT:
rev.pending.nr = rev.pending.alloc = 0;
@@ -658,6 +682,10 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
log_write_email_headers(rev, head, &subject_start, &extra_headers,
&need_8bit_cte);
+ for (i = 0; !need_8bit_cte && i < nr; i++)
+ if (has_non_ascii(list[i]->buffer))
+ need_8bit_cte = 1;
+
msg = body;
pp_user_info(NULL, CMIT_FMT_EMAIL, &sb, committer, DATE_RFC2822,
encoding);
diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c
index 92637ac..b0b5d8f 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 ad5f6b5..ee6ca0e 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-merge-base.c b/builtin-merge-base.c
index a6ec2f7..54e7ec2 100644
--- a/builtin-merge-base.c
+++ b/builtin-merge-base.c
@@ -23,7 +23,7 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
}
static const char * const merge_base_usage[] = {
- "git merge-base [--all] <commit-id> <commit-id>...",
+ "git merge-base [-a|--all] <commit> <commit>...",
NULL
};
diff --git a/builtin-merge.c b/builtin-merge.c
index 82b5466..b6b8428 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -358,6 +358,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
struct strbuf buf = STRBUF_INIT;
struct strbuf bname = STRBUF_INIT;
const char *ptr;
+ char *found_ref;
int len, early;
strbuf_branchname(&bname, remote);
@@ -368,14 +369,17 @@ static void merge_name(const char *remote, struct strbuf *msg)
if (!remote_head)
die("'%s' does not point to a commit", remote);
- strbuf_addstr(&buf, "refs/heads/");
- strbuf_addstr(&buf, remote);
- resolve_ref(buf.buf, branch_head, 0, NULL);
-
- if (!hashcmp(remote_head->sha1, branch_head)) {
- strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
- sha1_to_hex(branch_head), remote);
- goto cleanup;
+ if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) {
+ if (!prefixcmp(found_ref, "refs/heads/")) {
+ strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
+ sha1_to_hex(branch_head), remote);
+ goto cleanup;
+ }
+ if (!prefixcmp(found_ref, "refs/remotes/")) {
+ strbuf_addf(msg, "%s\t\tremote branch '%s' of .\n",
+ sha1_to_hex(branch_head), remote);
+ goto cleanup;
+ }
}
/* See if remote matches <name>^^^.. or <name>~<number> */
@@ -594,7 +598,7 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
discard_cache();
if (read_cache() < 0)
die("failed to read the cache");
- return -ret;
+ return ret;
}
}
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index ef4bf6b..c433748 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -86,7 +86,7 @@ static int pack_compression_level = Z_DEFAULT_COMPRESSION;
static int pack_compression_seen;
static unsigned long delta_cache_size = 0;
-static unsigned long max_delta_cache_size = 0;
+static unsigned long max_delta_cache_size = 256 * 1024 * 1024;
static unsigned long cache_max_small_delta_size = 1000;
static unsigned long window_memory_limit = 0;
@@ -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-packed.c b/builtin-prune-packed.c
index 00590b1..be99eb0 100644
--- a/builtin-prune-packed.c
+++ b/builtin-prune-packed.c
@@ -1,9 +1,12 @@
#include "builtin.h"
#include "cache.h"
#include "progress.h"
+#include "parse-options.h"
-static const char prune_packed_usage[] =
-"git prune-packed [-n] [-q]";
+static const char * const prune_packed_usage[] = {
+ "git prune-packed [-n|--dry-run] [-q|--quiet]",
+ NULL
+};
#define DRY_RUN 01
#define VERBOSE 02
@@ -68,24 +71,16 @@ void prune_packed_objects(int opts)
int cmd_prune_packed(int argc, const char **argv, const char *prefix)
{
- int i;
int opts = VERBOSE;
+ const struct option prune_packed_options[] = {
+ OPT_BIT('n', "dry-run", &opts, "dry run", DRY_RUN),
+ OPT_NEGBIT('q', "quiet", &opts, "be quiet", VERBOSE),
+ OPT_END()
+ };
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
+ argc = parse_options(argc, argv, prefix, prune_packed_options,
+ prune_packed_usage, 0);
- if (*arg == '-') {
- if (!strcmp(arg, "-n"))
- opts |= DRY_RUN;
- else if (!strcmp(arg, "-q"))
- opts &= ~VERBOSE;
- else
- usage(prune_packed_usage);
- continue;
- }
- /* Handle arguments here .. */
- usage(prune_packed_usage);
- }
prune_packed_objects(opts);
return 0;
}
diff --git a/builtin-prune.c b/builtin-prune.c
index 0ed9cce..8459aec 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-push.c b/builtin-push.c
index 1d92e22..67f6d96 100644
--- a/builtin-push.c
+++ b/builtin-push.c
@@ -140,6 +140,7 @@ static int do_push(const char *repo, int flags)
struct transport *transport =
transport_get(remote, url[i]);
int err;
+ int nonfastforward;
if (receivepack)
transport_set_option(transport,
TRANS_OPT_RECEIVEPACK, receivepack);
@@ -148,13 +149,19 @@ static int do_push(const char *repo, int flags)
if (flags & TRANSPORT_PUSH_VERBOSE)
fprintf(stderr, "Pushing to %s\n", url[i]);
- err = transport_push(transport, refspec_nr, refspec, flags);
+ err = transport_push(transport, refspec_nr, refspec, flags,
+ &nonfastforward);
err |= transport_disconnect(transport);
if (!err)
continue;
error("failed to push some refs to '%s'", url[i]);
+ if (nonfastforward) {
+ printf("To prevent you from losing history, non-fast-forward updates were rejected.\n"
+ "Merge the remote changes before pushing again.\n"
+ "See 'non-fast forward' section of 'git push --help' for details.\n");
+ }
errs++;
}
return !!errs;
@@ -168,6 +175,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
const char *repo = NULL; /* default repository */
struct option options[] = {
+ OPT_BIT('q', "quiet", &flags, "be quiet", TRANSPORT_PUSH_QUIET),
OPT_BIT('v', "verbose", &flags, "be verbose", TRANSPORT_PUSH_VERBOSE),
OPT_STRING( 0 , "repo", &repo, "repository", "repository"),
OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL),
diff --git a/builtin-read-tree.c b/builtin-read-tree.c
index 82e25ea..14c836b 100644
--- a/builtin-read-tree.c
+++ b/builtin-read-tree.c
@@ -12,6 +12,7 @@
#include "unpack-trees.h"
#include "dir.h"
#include "builtin.h"
+#include "parse-options.h"
static int nr_trees;
static struct tree *trees[MAX_UNPACK_TREES];
@@ -29,7 +30,39 @@ static int list_tree(unsigned char *sha1)
return 0;
}
-static const char read_tree_usage[] = "git read-tree (<sha> | [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u | -i]] [--exclude-per-directory=<gitignore>] [--index-output=<file>] <sha1> [<sha2> [<sha3>]])";
+static const char * const read_tree_usage[] = {
+ "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]",
+ NULL
+};
+
+static int index_output_cb(const struct option *opt, const char *arg,
+ int unset)
+{
+ set_alternate_index_output(arg);
+ return 0;
+}
+
+static int exclude_per_directory_cb(const struct option *opt, const char *arg,
+ int unset)
+{
+ struct dir_struct *dir;
+ struct unpack_trees_options *opts;
+
+ opts = (struct unpack_trees_options *)opt->value;
+
+ if (opts->dir)
+ die("more than one --exclude-per-directory given.");
+
+ dir = xcalloc(1, sizeof(*opts->dir));
+ dir->flags |= DIR_SHOW_IGNORED;
+ dir->exclude_per_dir = arg;
+ opts->dir = dir;
+ /* We do not need to nor want to do read-directory
+ * here; we are merely interested in reusing the
+ * per directory ignore stack mechanism.
+ */
+ return 0;
+}
static struct lock_file lock_file;
@@ -39,6 +72,34 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
unsigned char sha1[20];
struct tree_desc t[MAX_UNPACK_TREES];
struct unpack_trees_options opts;
+ int prefix_set = 0;
+ const struct option read_tree_options[] = {
+ { OPTION_CALLBACK, 0, "index-output", NULL, "FILE",
+ "write resulting index to <FILE>",
+ PARSE_OPT_NONEG, index_output_cb },
+ OPT__VERBOSE(&opts.verbose_update),
+ OPT_GROUP("Merging"),
+ OPT_SET_INT('m', NULL, &opts.merge,
+ "perform a merge in addition to a read", 1),
+ OPT_SET_INT(0, "trivial", &opts.trivial_merges_only,
+ "3-way merge if no file level merging required", 1),
+ OPT_SET_INT(0, "aggressive", &opts.aggressive,
+ "3-way merge in presence of adds and removes", 1),
+ OPT_SET_INT(0, "reset", &opts.reset,
+ "same as -m, but discard unmerged entries", 1),
+ { OPTION_STRING, 0, "prefix", &opts.prefix, "<subdirectory>/",
+ "read the tree into the index under <subdirectory>/",
+ PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP },
+ OPT_SET_INT('u', NULL, &opts.update,
+ "update working tree with merge result", 1),
+ { OPTION_CALLBACK, 0, "exclude-per-directory", &opts,
+ "gitignore",
+ "allow explicitly ignored files to be overwritten",
+ PARSE_OPT_NONEG, exclude_per_directory_cb },
+ OPT_SET_INT('i', NULL, &opts.index_only,
+ "don't check the working tree after merging", 1),
+ OPT_END()
+ };
memset(&opts, 0, sizeof(opts));
opts.head_idx = -1;
@@ -49,105 +110,21 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
newfd = hold_locked_index(&lock_file, 1);
- for (i = 1; i < argc; i++) {
- const char *arg = argv[i];
-
- /* "-u" means "update", meaning that a merge will update
- * the working tree.
- */
- if (!strcmp(arg, "-u")) {
- opts.update = 1;
- continue;
- }
+ argc = parse_options(argc, argv, unused_prefix, read_tree_options,
+ read_tree_usage, 0);
- if (!strcmp(arg, "-v")) {
- opts.verbose_update = 1;
- continue;
- }
+ prefix_set = opts.prefix ? 1 : 0;
+ if (1 < opts.merge + opts.reset + prefix_set)
+ die("Which one? -m, --reset, or --prefix?");
- /* "-i" means "index only", meaning that a merge will
- * not even look at the working tree.
- */
- if (!strcmp(arg, "-i")) {
- opts.index_only = 1;
- continue;
- }
-
- if (!prefixcmp(arg, "--index-output=")) {
- set_alternate_index_output(arg + 15);
- continue;
- }
-
- /* "--prefix=<subdirectory>/" means keep the current index
- * entries and put the entries from the tree under the
- * given subdirectory.
- */
- if (!prefixcmp(arg, "--prefix=")) {
- if (stage || opts.merge || opts.prefix)
- usage(read_tree_usage);
- opts.prefix = arg + 9;
- opts.merge = 1;
- stage = 1;
- if (read_cache_unmerged())
- die("you need to resolve your current index first");
- continue;
- }
-
- /* This differs from "-m" in that we'll silently ignore
- * unmerged entries and overwrite working tree files that
- * correspond to them.
- */
- if (!strcmp(arg, "--reset")) {
- if (stage || opts.merge || opts.prefix)
- usage(read_tree_usage);
- opts.reset = 1;
- opts.merge = 1;
- stage = 1;
- read_cache_unmerged();
- continue;
- }
-
- if (!strcmp(arg, "--trivial")) {
- opts.trivial_merges_only = 1;
- continue;
- }
-
- if (!strcmp(arg, "--aggressive")) {
- opts.aggressive = 1;
- continue;
- }
-
- /* "-m" stands for "merge", meaning we start in stage 1 */
- if (!strcmp(arg, "-m")) {
- if (stage || opts.merge || opts.prefix)
- usage(read_tree_usage);
- if (read_cache_unmerged())
- die("you need to resolve your current index first");
- stage = 1;
- opts.merge = 1;
- continue;
- }
-
- if (!prefixcmp(arg, "--exclude-per-directory=")) {
- struct dir_struct *dir;
-
- if (opts.dir)
- die("more than one --exclude-per-directory are given.");
-
- dir = xcalloc(1, sizeof(*opts.dir));
- dir->flags |= DIR_SHOW_IGNORED;
- dir->exclude_per_dir = arg + 24;
- opts.dir = dir;
- /* We do not need to nor want to do read-directory
- * here; we are merely interested in reusing the
- * per directory ignore stack mechanism.
- */
- continue;
- }
+ 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;
+ }
- /* using -u and -i at the same time makes no sense */
- if (1 < opts.index_only + opts.update)
- usage(read_tree_usage);
+ for (i = 0; i < argc; i++) {
+ const char *arg = argv[i];
if (get_sha1(arg, sha1))
die("Not a valid object name %s", arg);
@@ -155,8 +132,11 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
die("failed to unpack tree object %s", arg);
stage++;
}
+ if (1 < opts.index_only + opts.update)
+ die("-u and -i at the same time makes no sense");
if ((opts.update||opts.index_only) && !opts.merge)
- usage(read_tree_usage);
+ die("%s is meaningless without -m, --reset, or --prefix",
+ opts.update ? "-u" : "-i");
if ((opts.dir && !opts.update))
die("--exclude-per-directory is meaningless unless -u");
if (opts.merge && !opts.index_only)
diff --git a/builtin-receive-pack.c b/builtin-receive-pack.c
index 6ec1d05..b771fe9 100644
--- a/builtin-receive-pack.c
+++ b/builtin-receive-pack.c
@@ -123,31 +123,6 @@ static struct command *commands;
static const char pre_receive_hook[] = "hooks/pre-receive";
static const char post_receive_hook[] = "hooks/post-receive";
-static int run_status(int code, const char *cmd_name)
-{
- switch (code) {
- case 0:
- return 0;
- case -ERR_RUN_COMMAND_FORK:
- return error("fork of %s failed", cmd_name);
- case -ERR_RUN_COMMAND_EXEC:
- return error("execute of %s failed", cmd_name);
- case -ERR_RUN_COMMAND_PIPE:
- return error("pipe failed");
- case -ERR_RUN_COMMAND_WAITPID:
- return error("waitpid failed");
- case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
- return error("waitpid is confused");
- case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
- return error("%s died of signal", cmd_name);
- case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
- return error("%s died strangely", cmd_name);
- default:
- error("%s exited with error code %d", cmd_name, -code);
- return -code;
- }
-}
-
static int run_receive_hook(const char *hook_name)
{
static char buf[sizeof(commands->old_sha1) * 2 + PATH_MAX + 4];
@@ -174,7 +149,7 @@ static int run_receive_hook(const char *hook_name)
code = start_command(&proc);
if (code)
- return run_status(code, hook_name);
+ return code;
for (cmd = commands; cmd; cmd = cmd->next) {
if (!cmd->error_string) {
size_t n = snprintf(buf, sizeof(buf), "%s %s %s\n",
@@ -186,7 +161,7 @@ static int run_receive_hook(const char *hook_name)
}
}
close(proc.in);
- return run_status(finish_command(&proc), hook_name);
+ return finish_command(&proc);
}
static int run_update_hook(struct command *cmd)
@@ -203,9 +178,8 @@ static int run_update_hook(struct command *cmd)
argv[3] = sha1_to_hex(cmd->new_sha1);
argv[4] = NULL;
- return run_status(run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
- RUN_COMMAND_STDOUT_TO_STDERR),
- update_hook);
+ return run_command_v_opt(argv, RUN_COMMAND_NO_STDIN |
+ RUN_COMMAND_STDOUT_TO_STDERR);
}
static int is_ref_checked_out(const char *ref)
@@ -419,7 +393,6 @@ static void run_update_post_hook(struct command *cmd)
argv[argc] = NULL;
status = run_command_v_opt(argv, RUN_COMMAND_NO_STDIN
| RUN_COMMAND_STDOUT_TO_STDERR);
- run_status(status, update_post_hook);
}
static void execute_commands(const char *unpacker_error)
@@ -537,7 +510,6 @@ static const char *unpack(void)
code = run_command_v_opt(unpacker, RUN_GIT_CMD);
if (!code)
return NULL;
- run_status(code, unpacker[0]);
return "unpack-objects abnormal exit";
} else {
const char *keeper[7];
@@ -563,7 +535,6 @@ static const char *unpack(void)
ip.git_cmd = 1;
status = start_command(&ip);
if (status) {
- run_status(status, keeper[0]);
return "index-pack fork failed";
}
pack_lockfile = index_pack_lockfile(ip.out);
@@ -573,7 +544,6 @@ static const char *unpack(void)
reprepare_packed_git();
return NULL;
}
- run_status(status, keeper[0]);
return "index-pack abnormal exit";
}
}
diff --git a/builtin-reflog.c b/builtin-reflog.c
index ddfdf5a..95198c5 100644
--- a/builtin-reflog.c
+++ b/builtin-reflog.c
@@ -694,7 +694,7 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
*/
static const char reflog_usage[] =
-"git reflog (expire | ...)";
+"git reflog [ show | expire | delete ]";
int cmd_reflog(int argc, const char **argv, const char *prefix)
{
diff --git a/builtin-replace.c b/builtin-replace.c
new file mode 100644
index 0000000..fe3a647
--- /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-send-pack.c b/builtin-send-pack.c
index 47fb9f7..37e528e 100644
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
@@ -44,6 +44,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
NULL,
NULL,
NULL,
+ NULL,
};
struct child_process po;
int i;
@@ -53,6 +54,8 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
argv[i++] = "--thin";
if (args->use_ofs_delta)
argv[i++] = "--delta-base-offset";
+ if (args->quiet)
+ argv[i++] = "-q";
memset(&po, 0, sizeof(po));
po.argv = argv;
po.in = -1;
diff --git a/builtin-show-branch.c b/builtin-show-branch.c
index 01bea3b..3510a86 100644
--- a/builtin-show-branch.c
+++ b/builtin-show-branch.c
@@ -6,8 +6,8 @@
#include "parse-options.h"
static const char* show_branch_usage[] = {
- "git show-branch [--sparse] [--current] [--all] [--remotes] [--topo-order] [--more=count | --list | --independent | --merge-base] [--topics] [--color] [<refs>...]",
- "--reflog[=n[,b]] [--list] [--color] <branch>",
+ "git show-branch [-a|--all] [-r|--remotes] [--topo-order | --date-order] [--current] [--color | --no-color] [--sparse] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [--topics] [<rev> | <glob>]...",
+ "git show-branch (-g|--reflog)[=<n>[,<base>]] [--list] [<ref>]",
NULL
};
@@ -665,7 +665,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
OPT_BOOLEAN(0, "sha1-name", &sha1_name,
"name commits with their object names"),
OPT_BOOLEAN(0, "merge-base", &merge_base,
- "act like git merge-base -a"),
+ "show possible merge bases"),
OPT_BOOLEAN(0, "independent", &independent,
"show refs unreachable from any other ref"),
OPT_BOOLEAN(0, "topo-order", &lifo,
diff --git a/builtin-unpack-objects.c b/builtin-unpack-objects.c
index 557148a..685566e 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 0ee0a9a..b6079ae 100644
--- a/builtin-verify-pack.c
+++ b/builtin-verify-pack.c
@@ -2,15 +2,23 @@
#include "cache.h"
#include "pack.h"
#include "pack-revindex.h"
+#include "parse-options.h"
#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, chain_histogram[MAX_CHAIN+1];
+ 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;
memset(chain_histogram, 0, sizeof(chain_histogram));
+ baseobjects = 0;
for (i = 0; i < nr_objects; i++) {
const unsigned char *sha1;
@@ -28,14 +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 (!delta_chain_length)
- printf("%-6s %lu %lu %"PRIuMAX"\n",
- type, size, store_size, (uintmax_t)offset);
+ if (!stat_only)
+ printf("%s ", sha1_to_hex(sha1));
+ if (!delta_chain_length) {
+ 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
@@ -43,21 +56,29 @@ static void show_pack_info(struct packed_git *p)
}
}
- for (i = 0; i <= MAX_CHAIN; i++) {
- if (!chain_histogram[i])
+ if (baseobjects)
+ printf("non delta: %lu object%s\n",
+ baseobjects, baseobjects > 1 ? "s" : "");
+
+ for (cnt = 1; cnt <= MAX_CHAIN; cnt++) {
+ if (!chain_histogram[cnt])
continue;
- printf("chain length = %"PRIu32": %"PRIu32" object%s\n", i,
- chain_histogram[i], chain_histogram[i] > 1 ? "s" : "");
+ printf("chain length = %d: %lu object%s\n", cnt,
+ chain_histogram[cnt],
+ chain_histogram[cnt] > 1 ? "s" : "");
}
if (chain_histogram[0])
- printf("chain length > %d: %"PRIu32" object%s\n", MAX_CHAIN,
- chain_histogram[0], chain_histogram[0] > 1 ? "s" : "");
+ printf("chain length > %d: %lu object%s\n", MAX_CHAIN,
+ chain_histogram[0],
+ 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;
@@ -93,50 +114,53 @@ 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);
}
}
return err;
}
-static const char verify_pack_usage[] = "git verify-pack [-v] <pack>...";
+static const char * const verify_pack_usage[] = {
+ "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;
- int no_more_options = 0;
- int nothing_done = 1;
+ unsigned int flags = 0;
+ int i;
+ const struct option verify_pack_options[] = {
+ OPT_BIT('v', "verbose", &flags, "verbose",
+ VERIFY_PACK_VERBOSE),
+ OPT_BIT('s', "stat-only", &flags, "show statistics only",
+ VERIFY_PACK_STAT_ONLY),
+ OPT_END()
+ };
git_config(git_default_config, NULL);
- while (1 < argc) {
- if (!no_more_options && argv[1][0] == '-') {
- if (!strcmp("-v", argv[1]))
- verbose = 1;
- else if (!strcmp("--", argv[1]))
- no_more_options = 1;
- else
- usage(verify_pack_usage);
- }
- else {
- if (verify_one_pack(argv[1], verbose))
- err = 1;
- discard_revindex();
- nothing_done = 0;
- }
- argc--; argv++;
+ argc = parse_options(argc, argv, prefix, verify_pack_options,
+ verify_pack_usage, 0);
+ if (argc < 1)
+ usage_with_options(verify_pack_usage, verify_pack_options);
+ for (i = 0; i < argc; i++) {
+ if (verify_one_pack(argv[i], flags))
+ err = 1;
+ discard_revindex();
}
- if (nothing_done)
- usage(verify_pack_usage);
-
return err;
}
diff --git a/builtin-verify-tag.c b/builtin-verify-tag.c
index 7f7fda4..9f482c2 100644
--- a/builtin-verify-tag.c
+++ b/builtin-verify-tag.c
@@ -10,9 +10,12 @@
#include "tag.h"
#include "run-command.h"
#include <signal.h>
+#include "parse-options.h"
-static const char builtin_verify_tag_usage[] =
- "git verify-tag [-v|--verbose] <tag>...";
+static const char * const verify_tag_usage[] = {
+ "git verify-tag [-v|--verbose] <tag>...",
+ NULL
+};
#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
@@ -89,17 +92,17 @@ static int verify_tag(const char *name, int verbose)
int cmd_verify_tag(int argc, const char **argv, const char *prefix)
{
int i = 1, verbose = 0, had_error = 0;
+ const struct option verify_tag_options[] = {
+ OPT__VERBOSE(&verbose),
+ OPT_END()
+ };
git_config(git_default_config, NULL);
- if (argc > 1 &&
- (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))) {
- verbose = 1;
- i++;
- }
-
+ argc = parse_options(argc, argv, prefix, verify_tag_options,
+ verify_tag_usage, PARSE_OPT_KEEP_ARGV0);
if (argc <= i)
- usage(builtin_verify_tag_usage);
+ usage_with_options(verify_tag_usage, verify_tag_options);
/* sometimes the program was terminated because this signal
* was received in the process of writing the gpg input: */
diff --git a/builtin-write-tree.c b/builtin-write-tree.c
index 3a24ce8..b223af4 100644
--- a/builtin-write-tree.c
+++ b/builtin-write-tree.c
@@ -7,9 +7,12 @@
#include "cache.h"
#include "tree.h"
#include "cache-tree.h"
+#include "parse-options.h"
-static const char write_tree_usage[] =
-"git write-tree [--missing-ok] [--prefix=<prefix>/]";
+static const char * const write_tree_usage[] = {
+ "git write-tree [--missing-ok] [--prefix=<prefix>/]",
+ NULL
+};
int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
{
@@ -17,27 +20,22 @@ int cmd_write_tree(int argc, const char **argv, const char *unused_prefix)
const char *prefix = NULL;
unsigned char sha1[20];
const char *me = "git-write-tree";
+ struct option write_tree_options[] = {
+ OPT_BIT(0, "missing-ok", &flags, "allow missing objects",
+ WRITE_TREE_MISSING_OK),
+ { OPTION_STRING, 0, "prefix", &prefix, "<prefix>/",
+ "write tree object for a subdirectory <prefix>" ,
+ PARSE_OPT_LITERAL_ARGHELP },
+ { OPTION_BIT, 0, "ignore-cache-tree", &flags, NULL,
+ "only useful for debugging",
+ PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, NULL,
+ WRITE_TREE_IGNORE_CACHE_TREE },
+ OPT_END()
+ };
git_config(git_default_config, NULL);
- while (1 < argc) {
- const char *arg = argv[1];
- if (!strcmp(arg, "--missing-ok"))
- flags |= WRITE_TREE_MISSING_OK;
- else if (!prefixcmp(arg, "--prefix="))
- prefix = arg + 9;
- else if (!prefixcmp(arg, "--ignore-cache-tree"))
- /*
- * This is only useful for debugging, so I
- * do not bother documenting it.
- */
- flags |= WRITE_TREE_IGNORE_CACHE_TREE;
- else
- usage(write_tree_usage);
- argc--; argv++;
- }
-
- if (argc > 2)
- die("too many options");
+ argc = parse_options(argc, argv, unused_prefix, write_tree_options,
+ write_tree_usage, 0);
ret = write_cache_as_tree(sha1, flags, prefix);
switch (ret) {
diff --git a/builtin.h b/builtin.h
index 20427d2..51e4ba7 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 e6c7f33..dd7f71e 100644
--- a/cache.h
+++ b/cache.h
@@ -468,6 +468,9 @@ extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_obje
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
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, int skiplen);
+
#define REFRESH_REALLY 0x0001 /* ignore_valid */
#define REFRESH_UNMERGED 0x0002 /* allow unmerged */
#define REFRESH_QUIET 0x0004 /* be quiet about it */
@@ -509,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;
@@ -516,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;
@@ -652,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 e2bcbe8..a6c6f70 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 ba9f638..d1674d7 100644
--- a/commit.h
+++ b/commit.h
@@ -64,6 +64,7 @@ enum cmit_fmt {
};
extern int non_ascii(int);
+extern int has_non_ascii(const char *text);
struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
extern char *reencode_commit_message(const struct commit *commit,
const char **encoding_p);
@@ -122,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/bswap.h b/compat/bswap.h
new file mode 100644
index 0000000..7246a12
--- /dev/null
+++ b/compat/bswap.h
@@ -0,0 +1,36 @@
+/*
+ * Let's make sure we always have a sane definition for ntohl()/htonl().
+ * Some libraries define those as a function call, just to perform byte
+ * shifting, bringing significant overhead to what should be a simple
+ * operation.
+ */
+
+/*
+ * Default version that the compiler ought to optimize properly with
+ * constant values.
+ */
+static inline unsigned int default_swab32(unsigned int val)
+{
+ return (((val & 0xff000000) >> 24) |
+ ((val & 0x00ff0000) >> 8) |
+ ((val & 0x0000ff00) << 8) |
+ ((val & 0x000000ff) << 24));
+}
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+
+#define bswap32(x) ({ \
+ unsigned int __res; \
+ if (__builtin_constant_p(x)) { \
+ __res = default_swab32(x); \
+ } else { \
+ __asm__("bswap %0" : "=r" (__res) : "0" (x)); \
+ } \
+ __res; })
+
+#undef ntohl
+#undef htonl
+#define ntohl(x) bswap32(x)
+#define htonl(x) bswap32(x)
+
+#endif
diff --git a/compat/mingw.h b/compat/mingw.h
index c1859c5..948de66 100644
--- a/compat/mingw.h
+++ b/compat/mingw.h
@@ -17,9 +17,10 @@ typedef int pid_t;
#define S_IROTH 0
#define S_IXOTH 0
-#define WIFEXITED(x) ((unsigned)(x) < 259) /* STILL_ACTIVE */
+#define WIFEXITED(x) 1
+#define WIFSIGNALED(x) 0
#define WEXITSTATUS(x) ((x) & 0xff)
-#define WIFSIGNALED(x) ((unsigned)(x) > 259)
+#define WTERMSIG(x) SIGTERM
#define SIGHUP 1
#define SIGQUIT 3
diff --git a/compat/snprintf.c b/compat/snprintf.c
index 6c0fb05..4d07087 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/config.c b/config.c
index 738b244..e87edea 100644
--- a/config.c
+++ b/config.c
@@ -62,7 +62,8 @@ static char *parse_value(void)
if (comment)
continue;
if (isspace(c) && !quote) {
- space = 1;
+ if (len)
+ space++;
continue;
}
if (!quote) {
@@ -71,11 +72,8 @@ static char *parse_value(void)
continue;
}
}
- if (space) {
- if (len)
- value[len++] = ' ';
- space = 0;
- }
+ for (; space; space--)
+ value[len++] = ' ';
if (c == '\\') {
c = get_next_char();
switch (c) {
diff --git a/configure.ac b/configure.ac
index 3f1922d..b09b8e4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -156,19 +156,11 @@ AC_MSG_NOTICE([CHECKS for site configuration])
# tests. These tests take up a significant amount of the total test time
# but are not needed unless you plan to talk to SVN repos.
#
-# Define MOZILLA_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine coming from Mozilla. It is GPL'd and should be fast
-# on non-x86 architectures (e.g. PowerPC), while the OpenSSL version (default
-# choice) has very fast version optimized for i586.
-#
# Define PPC_SHA1 environment variable when running make to make use of
# a bundled SHA1 routine optimized for PowerPC.
#
-# Define ARM_SHA1 environment variable when running make to make use of
-# a bundled SHA1 routine optimized for ARM.
-#
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
-# This also implies MOZILLA_SHA1.
+# This also implies BLK_SHA1.
#
# Define OPENSSLDIR=/foo/bar if your openssl header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 745b5fb..bf688e1 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
@@ -1047,6 +1049,7 @@ _git_grep ()
--extended-regexp --basic-regexp --fixed-strings
--files-with-matches --name-only
--files-without-match
+ --max-depth
--count
--and --or --not --all-match
"
@@ -1536,6 +1539,7 @@ _git_config ()
__gitcomp "
add.ignore-errors
alias.
+ apply.ignorewhitespace
apply.whitespace
branch.autosetupmerge
branch.autosetuprebase
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index eace9c1..8c70ad8 100644
--- a/contrib/emacs/git.el
+++ b/contrib/emacs/git.el
@@ -429,16 +429,19 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)."
(git-get-string-sha1
(git-call-process-string-display-error "write-tree"))))
-(defun git-commit-tree (buffer tree head)
- "Call git-commit-tree with buffer as input and return the resulting commit SHA1."
+(defun git-commit-tree (buffer tree parent)
+ "Create a commit and possibly update HEAD.
+Create a commit with the message in BUFFER using the tree with hash TREE.
+Use PARENT as the parent of the new commit. If PARENT is the current \"HEAD\",
+update the \"HEAD\" reference to the new commit."
(let ((author-name (git-get-committer-name))
(author-email (git-get-committer-email))
(subject "commit (initial): ")
author-date log-start log-end args coding-system-for-write)
- (when head
+ (when parent
(setq subject "commit: ")
(push "-p" args)
- (push head args))
+ (push parent args))
(with-current-buffer buffer
(goto-char (point-min))
(if
@@ -474,7 +477,7 @@ Each entry is a cons of (SHORT-NAME . FULL-NAME)."
(apply #'git-run-command-region
buffer log-start log-end env
"commit-tree" tree (nreverse args))))))
- (when commit (git-update-ref "HEAD" commit head subject))
+ (when commit (git-update-ref "HEAD" commit parent subject))
commit)))
(defun git-empty-db-p ()
diff --git a/contrib/fast-import/git-p4 b/contrib/fast-import/git-p4
index 342529d..38438f3 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/contrib/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py
index 7b03204..2a6839d 100755
--- a/contrib/hg-to-git/hg-to-git.py
+++ b/contrib/hg-to-git/hg-to-git.py
@@ -20,7 +20,7 @@
"""
import os, os.path, sys
-import tempfile, popen2, pickle, getopt
+import tempfile, pickle, getopt
import re
# Maps hg version -> git version
diff --git a/convert.c b/convert.c
index 1816e97..491e714 100644
--- a/convert.c
+++ b/convert.c
@@ -267,7 +267,7 @@ static int filter_buffer(int fd, void *data)
status = finish_command(&child_process);
if (status)
- error("external filter %s failed %d", params->cmd, -status);
+ error("external filter %s failed %d", params->cmd, status);
return (write_err || status);
}
diff --git a/date.c b/date.c
index 409a17d..f011692 100644
--- a/date.c
+++ b/date.c
@@ -135,7 +135,7 @@ const char *show_date(unsigned long time, int tz, enum date_mode mode)
}
/* Give years and months for 5 years or so */
if (diff < 1825) {
- unsigned long years = (diff + 183) / 365;
+ unsigned long years = diff / 365;
unsigned long months = (diff % 365 + 15) / 30;
int n;
n = snprintf(timebuf, sizeof(timebuf), "%lu year%s",
diff --git a/diff-lib.c b/diff-lib.c
index 2a82dac..0c74ef5 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);
@@ -321,6 +322,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".
@@ -328,7 +331,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/dir.c b/dir.c
index e05b850..d0999ba 100644
--- a/dir.c
+++ b/dir.c
@@ -861,12 +861,20 @@ int is_empty_dir(const char *path)
return ret;
}
-int remove_dir_recursively(struct strbuf *path, int only_empty)
+int remove_dir_recursively(struct strbuf *path, int flag)
{
- DIR *dir = opendir(path->buf);
+ DIR *dir;
struct dirent *e;
int ret = 0, original_len = path->len, len;
+ int only_empty = (flag & REMOVE_DIR_EMPTY_ONLY);
+ unsigned char submodule_head[20];
+ if ((flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
+ !resolve_gitlink_ref(path->buf, "HEAD", submodule_head))
+ /* Do not descend and nuke a nested git work tree. */
+ return 0;
+
+ dir = opendir(path->buf);
if (!dir)
return -1;
if (path->buf[original_len - 1] != '/')
diff --git a/dir.h b/dir.h
index a631446..320b6a2 100644
--- a/dir.h
+++ b/dir.h
@@ -88,7 +88,10 @@ static inline int is_dot_or_dotdot(const char *name)
extern int is_empty_dir(const char *dir);
extern void setup_standard_excludes(struct dir_struct *dir);
-extern int remove_dir_recursively(struct strbuf *path, int only_empty);
+
+#define REMOVE_DIR_EMPTY_ONLY 01
+#define REMOVE_DIR_KEEP_NESTED_GIT 02
+extern int remove_dir_recursively(struct strbuf *path, int flag);
/* tries to remove the path with empty directories along it, ignores ENOENT */
extern int remove_path(const char *path);
diff --git a/entry.c b/entry.c
index d3e86c7..06d24f1 100644
--- a/entry.c
+++ b/entry.c
@@ -175,6 +175,23 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
return 0;
}
+/*
+ * This is like 'lstat()', except it refuses to follow symlinks
+ * in the path, after skipping "skiplen".
+ */
+int check_path(const char *path, int len, struct stat *st, int skiplen)
+{
+ const char *slash = path + len;
+
+ while (path < slash && *slash != '/')
+ slash--;
+ if (!has_dirs_only_path(path, slash - path, skiplen)) {
+ errno = ENOENT;
+ return -1;
+ }
+ return lstat(path, st);
+}
+
int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath)
{
static char path[PATH_MAX + 1];
@@ -188,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 (!lstat(path, &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 8f5eaa7..5de6837 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 df9f231..06f7060 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 d64d997..3c03f3e 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
@@ -191,13 +193,33 @@ check_patch_format () {
esac
;;
esac
+ if test -z "$patch_format" &&
+ test -n "$l1" &&
+ test -n "$l2" &&
+ test -n "$l3"
+ then
+ # This begins with three non-empty lines. Is this a
+ # piece of e-mail a-la RFC2822? Grab all the headers,
+ # discarding the indented remainder of folded lines,
+ # and see if it looks like that they all begin with the
+ # header field names...
+ sed -n -e '/^$/q' -e '/^[ ]/d' -e p "$1" |
+ egrep -v '^[A-Za-z]+(-[A-Za-z]+)*:' >/dev/null ||
+ patch_format=mbox
+ fi
} < "$1" || clean_abort
}
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)
@@ -254,7 +276,11 @@ split_patches () {
msgnum=
;;
*)
- clean_abort "Patch format $patch_format is not supported."
+ if test -n "$parse_patch" ; then
+ clean_abort "Patch format $patch_format is not supported."
+ else
+ clean_abort "Patch format detection failed."
+ fi
;;
esac
}
@@ -303,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 8969553..6f6f039 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 9f941e4..e5e9f39 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
@@ -176,6 +174,8 @@ extern char *gitbasename(char *);
#endif
#endif
+#include "compat/bswap.h"
+
/* General helper functions */
extern void usage(const char *err) NORETURN;
extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
diff --git a/git-cvsimport.perl b/git-cvsimport.perl
index e439202..593832d 100755
--- a/git-cvsimport.perl
+++ b/git-cvsimport.perl
@@ -252,7 +252,8 @@ sub conn {
}
};
}
- $pass="A" unless $pass;
+
+ $pass = $self->_scramble($pass);
my ($s, $rep);
if ($proxyhost) {
@@ -484,6 +485,42 @@ sub _fetchfile {
return $res;
}
+sub _scramble {
+ my ($self, $pass) = @_;
+ my $scrambled = "A";
+
+ return $scrambled unless $pass;
+
+ my $pass_len = length($pass);
+ my @pass_arr = split("", $pass);
+ my $i;
+
+ # from cvs/src/scramble.c
+ my @shifts = (
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 114,120, 53, 79, 96,109, 72,108, 70, 64, 76, 67,116, 74, 68, 87,
+ 111, 52, 75,119, 49, 34, 82, 81, 95, 65,112, 86,118,110,122,105,
+ 41, 57, 83, 43, 46,102, 40, 89, 38,103, 45, 50, 42,123, 91, 35,
+ 125, 55, 54, 66,124,126, 59, 47, 92, 71,115, 78, 88,107,106, 56,
+ 36,121,117,104,101,100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
+ 58,113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85,223,
+ 225,216,187,166,229,189,222,188,141,249,148,200,184,136,248,190,
+ 199,170,181,204,138,232,218,183,255,234,220,247,213,203,226,193,
+ 174,172,228,252,217,201,131,230,197,211,145,238,161,179,160,212,
+ 207,221,254,173,202,146,224,151,140,196,205,130,135,133,143,246,
+ 192,159,244,239,185,168,215,144,139,165,180,157,147,186,214,176,
+ 227,231,219,169,175,156,206,198,129,164,150,210,154,177,134,127,
+ 182,128,158,208,162,132,167,209,149,241,153,251,237,236,171,195,
+ 243,233,253,240,194,250,191,155,142,137,245,235,163,242,178,152
+ );
+
+ for ($i = 0; $i < $pass_len; $i++) {
+ $scrambled .= pack("C", $shifts[ord($pass_arr[$i])]);
+ }
+
+ return $scrambled;
+}
package main;
diff --git a/git-filter-branch.sh b/git-filter-branch.sh
index 37e044d..a480d6f 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 32f6496..d96eddb 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 () {
@@ -278,7 +297,7 @@ EOF
# check to see if Dennis Stosberg's mod_perl compatibility patch
# (<20060621130708.Gcbc6e5c@leonov.stosberg.net>) has been applied
- if test -f "$module_path/mod_perl.so" && grep '^our $gitbin' \
+ if test -f "$module_path/mod_perl.so" && grep 'MOD_PERL' \
"$GIT_DIR/gitweb/gitweb.cgi" >/dev/null
then
# favor mod_perl if available
@@ -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-pull.sh b/git-pull.sh
index 4b78a0c..0bbd5bf 100755
--- a/git-pull.sh
+++ b/git-pull.sh
@@ -119,15 +119,31 @@ error_on_no_merge_candidates () {
}
test true = "$rebase" && {
- git update-index --ignore-submodules --refresh &&
- git diff-files --ignore-submodules --quiet &&
- git diff-index --ignore-submodules --cached --quiet HEAD -- ||
- die "refusing to pull with rebase: your working tree is not up-to-date"
-
+ if ! git rev-parse -q --verify HEAD >/dev/null
+ then
+ # On an unborn branch
+ if test -f "$GIT_DIR/index"
+ then
+ die "updating an unborn branch with changes added to the index"
+ fi
+ else
+ git update-index --ignore-submodules --refresh &&
+ git diff-files --ignore-submodules --quiet &&
+ git diff-index --ignore-submodules --cached --quiet HEAD -- ||
+ die "refusing to pull with rebase: your working tree is not up-to-date"
+ fi
+ oldremoteref= &&
. git-parse-remote &&
- reflist="$(get_remote_merge_branch "$@" 2>/dev/null)" &&
- oldremoteref="$(git rev-parse -q --verify \
- "$reflist")"
+ remoteref="$(get_remote_merge_branch "$@" 2>/dev/null)" &&
+ oldremoteref="$(git rev-parse -q --verify "$remoteref")" &&
+ for reflog in $(git rev-list -g $remoteref 2>/dev/null)
+ do
+ if test "$reflog" = "$(git merge-base $reflog $curr_branch)"
+ then
+ oldremoteref="$reflog"
+ break
+ fi
+ done
}
orig_head=$(git rev-parse -q --verify HEAD)
git fetch $verbosity --update-head-ok "$@" || exit 1
diff --git a/git-rebase.sh b/git-rebase.sh
index 18bc694..2315d95 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
@@ -382,8 +385,10 @@ else
fi
# The tree must be really really clean.
-if ! git update-index --ignore-submodules --refresh; then
- die "cannot rebase: you have unstaged changes"
+if ! git update-index --ignore-submodules --refresh > /dev/null; then
+ echo >&2 "cannot rebase: you have unstaged changes"
+ git diff --name-status -r --ignore-submodules -- >&2
+ exit 1
fi
diff=$(git diff-index --cached --name-status -r --ignore-submodules HEAD --)
case "$diff" in
diff --git a/git-send-email.perl b/git-send-email.perl
index d508f83..0700d80 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -450,7 +450,6 @@ sub check_file_rev_conflict($) {
try {
$repo->command('rev-parse', '--verify', '--quiet', $f);
if (defined($format_patch)) {
- print "foo\n";
return $format_patch;
}
die(<<EOF);
diff --git a/git-stash.sh b/git-stash.sh
index 03e589f..d61c9d0 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 ebed711..bfbd36b 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -4,9 +4,14 @@
#
# Copyright (c) 2007 Lars Hjemli
-USAGE="[--quiet] [--cached] \
-[add [-b branch] <repo> <path>]|[status|init|update [-i|--init] [-N|--no-fetch] [--rebase|--merge]|summary [-n|--summary-limit <n>] [<commit>]] \
-[--] [<path>...]|[foreach <command>]|[sync [--] [<path>...]]"
+dashless=$(basename "$0" | sed -e 's/-/ /')
+USAGE="[--quiet] add [-b branch] [--reference <repository>] [--] <repository> <path>
+ 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|--files] [--summary-limit <n>] [commit] [--] [<path>...]
+ or: $dashless [--quiet] foreach [--recursive] <command>
+ or: $dashless [--quiet] sync [--] [<path>...]"
OPTIONS_SPEC=
. git-sh-setup
. git-parse-remote
@@ -16,8 +21,10 @@ command=
branch=
reference=
cached=
+files=
nofetch=
update=
+prefix=
# Resolve relative url by appending to parent's url
resolve_relative_url ()
@@ -237,13 +244,43 @@ cmd_add()
#
cmd_foreach()
{
+ # parse $args after "submodule ... foreach".
+ while test $# -ne 0
+ do
+ case "$1" in
+ -q|--quiet)
+ GIT_QUIET=1
+ ;;
+ --recursive)
+ recursive=1
+ ;;
+ -*)
+ usage
+ ;;
+ *)
+ break
+ ;;
+ esac
+ shift
+ done
+
module_list |
while read mode sha1 stage path
do
if test -e "$path"/.git
then
- say "Entering '$path'"
- (cd "$path" && eval "$@") ||
+ say "Entering '$prefix$path'"
+ name=$(module_name "$path")
+ (
+ prefix="$prefix$path/"
+ unset GIT_DIR
+ cd "$path" &&
+ eval "$@" &&
+ if test -n "$recursive"
+ then
+ cmd_foreach "--recursive" "$@"
+ fi
+ ) ||
die "Stopping at '$path'; script returned non-zero status."
fi
done
@@ -316,6 +353,7 @@ cmd_init()
cmd_update()
{
# parse $args after "submodule ... update".
+ orig_args="$@"
while test $# -ne 0
do
case "$1" in
@@ -348,6 +386,10 @@ cmd_update()
shift
update="merge"
;;
+ --recursive)
+ shift
+ recursive=1
+ ;;
--)
shift
break
@@ -434,6 +476,12 @@ cmd_update()
die "Unable to $action '$sha1' in submodule path '$path'"
say "Submodule path '$path': $msg '$sha1'"
fi
+
+ if test -n "$recursive"
+ then
+ (unset GIT_DIR; cd "$path" && cmd_update $orig_args) ||
+ die "Failed to recurse into submodule path '$path'"
+ fi
done
}
@@ -460,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
@@ -468,6 +517,9 @@ cmd_summary() {
--cached)
cached="$1"
;;
+ --files)
+ files="$1"
+ ;;
--for-status)
for_status="$1"
;;
@@ -504,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
@@ -520,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
@@ -643,6 +703,7 @@ cmd_summary() {
cmd_status()
{
# parse $args after "submodule ... status".
+ orig_args="$@"
while test $# -ne 0
do
case "$1" in
@@ -652,6 +713,9 @@ cmd_status()
--cached)
cached=1
;;
+ --recursive)
+ recursive=1
+ ;;
--)
shift
break
@@ -671,22 +735,34 @@ cmd_status()
do
name=$(module_name "$path") || exit
url=$(git config submodule."$name".url)
+ displaypath="$prefix$path"
if test -z "$url" || ! test -d "$path"/.git -o -f "$path"/.git
then
- say "-$sha1 $path"
+ say "-$sha1 $displaypath"
continue;
fi
set_name_rev "$path" "$sha1"
if git diff-files --quiet -- "$path"
then
- say " $sha1 $path$revname"
+ say " $sha1 $displaypath$revname"
else
if test -z "$cached"
then
sha1=$(unset GIT_DIR; cd "$path" && git rev-parse --verify HEAD)
set_name_rev "$path" "$sha1"
fi
- say "+$sha1 $path$revname"
+ say "+$sha1 $displaypath$revname"
+ fi
+
+ if test -n "$recursive"
+ then
+ (
+ prefix="$displaypath/"
+ unset GIT_DIR
+ cd "$path" &&
+ cmd_status $orig_args
+ ) ||
+ die "Failed to recurse into submodule path '$path'"
fi
done
}
diff --git a/git-svn.perl b/git-svn.perl
index d075810..ce4fef9 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
@@ -764,6 +773,7 @@ sub cmd_show_ignore {
print STDOUT "\n# $path\n";
my $s = $props->{'svn:ignore'} or return;
$s =~ s/[\r\n]+/\n/g;
+ $s =~ s/^\n+//;
chomp $s;
$s =~ s#^#$path#gm;
print STDOUT "$s\n";
@@ -801,6 +811,7 @@ sub cmd_create_ignore {
open(GITIGNORE, '>', $ignore)
or fatal("Failed to open `$ignore' for writing: $!");
$s =~ s/[\r\n]+/\n/g;
+ $s =~ s/^\n+//;
chomp $s;
# Prefix all patterns so that the ignore doesn't apply
# to sub-directories.
@@ -907,7 +918,7 @@ sub cmd_multi_init {
}
do_git_init_db();
if (defined $_trunk) {
- my $trunk_ref = $_prefix . 'trunk';
+ my $trunk_ref = 'refs/remotes/' . $_prefix . 'trunk';
# try both old-style and new-style lookups:
my $gs_trunk = eval { Git::SVN->new($trunk_ref) };
unless ($gs_trunk) {
@@ -1154,6 +1165,17 @@ sub post_fetch_checkout {
my $gs = $Git::SVN::_head or return;
return if verify_ref('refs/heads/master^0');
+ # look for "trunk" ref if it exists
+ my $remote = Git::SVN::read_all_remotes()->{$gs->{repo_id}};
+ my $fetch = $remote->{fetch};
+ if ($fetch) {
+ foreach my $p (keys %$fetch) {
+ basename($fetch->{$p}) eq 'trunk' or next;
+ $gs = Git::SVN->new($fetch->{$p}, $gs->{repo_id}, $p);
+ last;
+ }
+ }
+
my $valid_head = verify_ref('HEAD^0');
command_noisy(qw(update-ref refs/heads/master), $gs->refname);
return if ($valid_head || !verify_ref('HEAD^0'));
@@ -1209,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 !~ /\*/;
@@ -1641,23 +1664,23 @@ sub resolve_local_globs {
return unless defined $glob_spec;
my $ref = $glob_spec->{ref};
my $path = $glob_spec->{path};
- foreach (command(qw#for-each-ref --format=%(refname) refs/remotes#)) {
- next unless m#^refs/remotes/$ref->{regex}$#;
+ foreach (command(qw#for-each-ref --format=%(refname) refs/#)) {
+ next unless m#^$ref->{regex}$#;
my $p = $1;
my $pathname = desanitize_refname($path->full_path($p));
my $refname = desanitize_refname($ref->full_path($p));
if (my $existing = $fetch->{$pathname}) {
if ($existing ne $refname) {
die "Refspec conflict:\n",
- "existing: refs/remotes/$existing\n",
- " globbed: refs/remotes/$refname\n";
+ "existing: $existing\n",
+ " globbed: $refname\n";
}
- my $u = (::cmt_metadata("refs/remotes/$refname"))[0];
+ my $u = (::cmt_metadata("$refname"))[0];
$u =~ s!^\Q$url\E(/|$)!! or die
- "refs/remotes/$refname: '$url' not found in '$u'\n";
+ "$refname: '$url' not found in '$u'\n";
if ($pathname ne $u) {
warn "W: Refspec glob conflict ",
- "(ref: refs/remotes/$refname):\n",
+ "(ref: $refname):\n",
"expected path: $pathname\n",
" real path: $u\n",
"Continuing ahead with $u\n";
@@ -1735,33 +1758,35 @@ sub read_all_remotes {
my $use_svm_props = eval { command_oneline(qw/config --bool
svn.useSvmProps/) };
$use_svm_props = $use_svm_props eq 'true' if $use_svm_props;
+ my $svn_refspec = qr{\s*/?(.*?)\s*:\s*(.+?)\s*};
foreach (grep { s/^svn-remote\.// } command(qw/config -l/)) {
- if (m!^(.+)\.fetch=\s*(.*)\s*:\s*(.+)\s*$!) {
- my ($remote, $local_ref, $_remote_ref) = ($1, $2, $3);
- die("svn-remote.$remote: remote ref '$_remote_ref' "
- . "must start with 'refs/remotes/'\n")
- unless $_remote_ref =~ m{^refs/remotes/(.+)};
- my $remote_ref = $1;
- $local_ref =~ s{^/}{};
+ if (m!^(.+)\.fetch=$svn_refspec$!) {
+ my ($remote, $local_ref, $remote_ref) = ($1, $2, $3);
+ die("svn-remote.$remote: remote ref '$remote_ref' "
+ . "must start with 'refs/'\n")
+ unless $remote_ref =~ m{^refs/};
$r->{$remote}->{fetch}->{$local_ref} = $remote_ref;
$r->{$remote}->{svm} = {} if $use_svm_props;
} elsif (m!^(.+)\.usesvmprops=\s*(.*)\s*$!) {
$r->{$1}->{svm} = {};
} elsif (m!^(.+)\.url=\s*(.*)\s*$!) {
$r->{$1}->{url} = $2;
- } elsif (m!^(.+)\.(branches|tags)=
- (.*):refs/remotes/(.+)\s*$/!x) {
- my ($p, $g) = ($3, $4);
+ } elsif (m!^(.+)\.(branches|tags)=$svn_refspec$!) {
+ my ($remote, $t, $local_ref, $remote_ref) =
+ ($1, $2, $3, $4);
+ die("svn-remote.$remote: remote ref '$remote_ref' ($t) "
+ . "must start with 'refs/'\n")
+ unless $remote_ref =~ m{^refs/};
my $rs = {
- t => $2,
- remote => $1,
- path => Git::SVN::GlobSpec->new($p),
- ref => Git::SVN::GlobSpec->new($g) };
+ t => $t,
+ remote => $remote,
+ path => Git::SVN::GlobSpec->new($local_ref),
+ ref => Git::SVN::GlobSpec->new($remote_ref) };
if (length($rs->{ref}->{right}) != 0) {
die "The '*' glob character must be the last ",
- "character of '$g'\n";
+ "character of '$remote_ref'\n";
}
- push @{ $r->{$1}->{$2} }, $rs;
+ push @{ $r->{$remote}->{$t} }, $rs;
}
}
@@ -1869,14 +1894,15 @@ sub init_remote_config {
}
}
my ($xrepo_id, $xpath) = find_ref($self->refname);
- if (defined $xpath) {
+ if (!$no_write && defined $xpath) {
die "svn-remote.$xrepo_id.fetch already set to track ",
- "$xpath:refs/remotes/", $self->refname, "\n";
+ "$xpath:", $self->refname, "\n";
}
unless ($no_write) {
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);
@@ -1946,7 +1972,7 @@ sub find_ref {
my ($ref_id) = @_;
foreach (command(qw/config -l/)) {
next unless m!^svn-remote\.(.+)\.fetch=
- \s*(.*)\s*:\s*refs/remotes/(.+)\s*$!x;
+ \s*/?(.*?)\s*:\s*(.+?)\s*$!x;
my ($repo_id, $path, $ref) = ($1, $2, $3);
if ($ref eq $ref_id) {
$path = '' if ($path =~ m#^\./?#);
@@ -1963,16 +1989,16 @@ sub new {
if (!defined $repo_id) {
die "Could not find a \"svn-remote.*.fetch\" key ",
"in the repository configuration matching: ",
- "refs/remotes/$ref_id\n";
+ "$ref_id\n";
}
}
my $self = _new($class, $repo_id, $ref_id, $path);
if (!defined $self->{path} || !length $self->{path}) {
my $fetch = command_oneline('config', '--get',
"svn-remote.$repo_id.fetch",
- ":refs/remotes/$ref_id\$") or
+ ":$ref_id\$") or
die "Failed to read \"svn-remote.$repo_id.fetch\" ",
- "\":refs/remotes/$ref_id\$\" in config\n";
+ "\":$ref_id\$\" in config\n";
($self->{path}, undef) = split(/\s*:\s*/, $fetch);
}
$self->{url} = command_oneline('config', '--get',
@@ -1983,7 +2009,7 @@ sub new {
}
sub refname {
- my ($refname) = "refs/remotes/$_[0]->{ref_id}" ;
+ my ($refname) = $_[0]->{ref_id} ;
# It cannot end with a slash /, we'll throw up on this because
# SVN can't have directories with a slash in their name, either:
@@ -3262,7 +3288,7 @@ sub _rev_map_get {
my $i = int(($l/24 + $u/24) / 2) * 24;
sysseek($fh, $i, SEEK_SET) or croak "seek: $!";
sysread($fh, my $buf, 24) == 24 or croak "read: $!";
- my ($r, $c) = unpack('NH40', $buf);
+ my ($r, $c) = unpack(rev_map_fmt, $buf);
if ($r < $rev) {
$l = $i + 24;
@@ -3317,12 +3343,24 @@ sub _new {
$repo_id = $Git::SVN::default_repo_id;
}
unless (defined $ref_id && length $ref_id) {
- $_[2] = $ref_id = $Git::SVN::default_ref_id;
+ $_prefix = '' unless defined($_prefix);
+ $_[2] = $ref_id =
+ "refs/remotes/$_prefix$Git::SVN::default_ref_id";
}
$_[1] = $repo_id;
my $dir = "$ENV{GIT_DIR}/svn/$ref_id";
+
+ # Older repos imported by us used $GIT_DIR/svn/foo instead of
+ # $GIT_DIR/svn/refs/remotes/foo when tracking refs/remotes/foo
+ if ($ref_id =~ m{^refs/remotes/(.*)}) {
+ my $old_dir = "$ENV{GIT_DIR}/svn/$1";
+ if (-d $old_dir && ! -d $dir) {
+ $dir = $old_dir;
+ }
+ }
+
$_[3] = $path = '' unless (defined $path);
- mkpath(["$ENV{GIT_DIR}/svn"]);
+ mkpath([$dir]);
bless {
ref_id => $ref_id, dir => $dir, index => "$dir/index",
path => $path, config => "$ENV{GIT_DIR}/svn/config",
@@ -5495,7 +5533,7 @@ sub minimize_connections {
my $pfx = "svn-remote.$x->{old_repo_id}";
my $old_fetch = quotemeta("$x->{old_path}:".
- "refs/remotes/$x->{ref_id}");
+ "$x->{ref_id}");
command_noisy(qw/config --unset/,
"$pfx.fetch", '^'. $old_fetch . '$');
delete $r->{$x->{old_repo_id}}->
@@ -5564,7 +5602,7 @@ sub new {
my ($class, $glob) = @_;
my $re = $glob;
$re =~ s!/+$!!g; # no need for trailing slashes
- $re =~ m!^([^*]*)(\*(?:/\*)*)([^*]*)$!;
+ $re =~ m!^([^*]*)(\*(?:/\*)*)(.*)$!;
my $temp = $re;
my ($left, $right) = ($1, $3);
$re = $2;
diff --git a/git.c b/git.c
index 807d875..5da6c65 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 },
@@ -416,13 +417,9 @@ static void execv_dashed_external(const char **argv)
* if we fail because the command is not found, it is
* OK to return. Otherwise, we just pass along the status code.
*/
- status = run_command_v_opt(argv, 0);
- if (status != -ERR_RUN_COMMAND_EXEC) {
- if (IS_RUN_COMMAND_ERR(status))
- die("unable to run '%s'", argv[0]);
- exit(-status);
- }
- errno = ENOENT; /* as if we called execvp */
+ status = run_command_v_opt(argv, RUN_SILENT_EXEC_FAILURE);
+ if (status >= 0 || errno != ENOENT)
+ exit(status);
argv[0] = tmp;
diff --git a/gitk-git/gitk b/gitk-git/gitk
index 4604c83..8c08310 100644
--- a/gitk-git/gitk
+++ b/gitk-git/gitk
@@ -288,7 +288,7 @@ proc parseviewrevs {view revs} {
if {$sdm != 2} {
lappend ret $id
} else {
- lset ret end [lindex $ret end]...$id
+ lset ret end $id...[lindex $ret end]
}
lappend pos $id
}
@@ -1677,6 +1677,7 @@ proc readrefs {} {
global tagids idtags headids idheads tagobjid
global otherrefids idotherrefs mainhead mainheadid
global selecthead selectheadid
+ global hideremotes
foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
catch {unset $v}
@@ -1689,7 +1690,7 @@ proc readrefs {} {
if {![string match "refs/*" $ref]} continue
set name [string range $ref 5 end]
if {[string match "remotes/*" $name]} {
- if {![string match "*/HEAD" $name]} {
+ if {![string match "*/HEAD" $name] && !$hideremotes} {
set headids($name) $id
lappend idheads($id) $name
}
@@ -2520,6 +2521,7 @@ proc savestuff {w} {
global cmitmode wrapcomment datetimeformat limitdiffs
global colors bgcolor fgcolor diffcolors diffcontext selectbgcolor
global autoselect extdifftool perfile_attrs markbgcolor
+ global hideremotes
if {$stuffsaved} return
if {![winfo viewable .]} return
@@ -2539,6 +2541,7 @@ proc savestuff {w} {
puts $f [list set wrapcomment $wrapcomment]
puts $f [list set autoselect $autoselect]
puts $f [list set showneartags $showneartags]
+ puts $f [list set hideremotes $hideremotes]
puts $f [list set showlocalchanges $showlocalchanges]
puts $f [list set datetimeformat $datetimeformat]
puts $f [list set limitdiffs $limitdiffs]
@@ -7906,6 +7909,11 @@ proc gotocommit {} {
}
set id [lindex $matches 0]
}
+ } else {
+ if {[catch {set id [exec git rev-parse --verify $sha1string]}]} {
+ error_popup [mc "Revision %s is not known" $sha1string]
+ return
+ }
}
}
if {[commitinview $id $curview]} {
@@ -7915,7 +7923,7 @@ proc gotocommit {} {
if {[regexp {^[0-9a-fA-F]{4,}$} $sha1string]} {
set msg [mc "SHA1 id %s is not known" $sha1string]
} else {
- set msg [mc "Tag/Head %s is not known" $sha1string]
+ set msg [mc "Revision %s is not in the current view" $sha1string]
}
error_popup $msg
}
@@ -10383,6 +10391,7 @@ proc doprefs {} {
global oldprefs prefstop showneartags showlocalchanges
global bgcolor fgcolor ctext diffcolors selectbgcolor markbgcolor
global tabstop limitdiffs autoselect extdifftool perfile_attrs
+ global hideremotes
set top .gitkprefs
set prefstop $top
@@ -10391,7 +10400,7 @@ proc doprefs {} {
return
}
foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
- limitdiffs tabstop perfile_attrs} {
+ limitdiffs tabstop perfile_attrs hideremotes} {
set oldprefs($v) [set $v]
}
toplevel $top
@@ -10423,6 +10432,9 @@ proc doprefs {} {
checkbutton $top.ntag -text [mc "Display nearby tags"] \
-font optionfont -variable showneartags
grid x $top.ntag -sticky w
+ checkbutton $top.hideremotes -text [mc "Hide remote refs"] \
+ -font optionfont -variable hideremotes
+ grid x $top.hideremotes -sticky w
checkbutton $top.ldiff -text [mc "Limit diffs to listed paths"] \
-font optionfont -variable limitdiffs
grid x $top.ldiff -sticky w
@@ -10547,7 +10559,7 @@ proc prefscan {} {
global oldprefs prefstop
foreach v {maxwidth maxgraphpct showneartags showlocalchanges \
- limitdiffs tabstop perfile_attrs} {
+ limitdiffs tabstop perfile_attrs hideremotes} {
global $v
set $v $oldprefs($v)
}
@@ -10561,6 +10573,7 @@ proc prefsok {} {
global oldprefs prefstop showneartags showlocalchanges
global fontpref mainfont textfont uifont
global limitdiffs treediffs perfile_attrs
+ global hideremotes
catch {destroy $prefstop}
unset prefstop
@@ -10606,6 +10619,9 @@ proc prefsok {} {
$limitdiffs != $oldprefs(limitdiffs)} {
reselectline
}
+ if {$hideremotes != $oldprefs(hideremotes)} {
+ rereadrefs
+ }
}
proc formatdate {d} {
@@ -10901,7 +10917,7 @@ proc gitattr {path attr default} {
} else {
set r "unspecified"
if {![catch {set line [exec git check-attr $attr -- $path]}]} {
- regexp "(.*): encoding: (.*)" $line m f r
+ regexp "(.*): $attr: (.*)" $line m f r
}
set path_attr_cache($attr,$path) $r
}
@@ -10929,7 +10945,7 @@ proc cache_gitattr {attr pathlist} {
set newlist [lrange $newlist $lim end]
if {![catch {set rlist [eval exec git check-attr $attr -- $head]}]} {
foreach row [split $rlist "\n"] {
- if {[regexp "(.*): encoding: (.*)" $row m path value]} {
+ if {[regexp "(.*): $attr: (.*)" $row m path value]} {
if {[string index $path 0] eq "\""} {
set path [encoding convertfrom [lindex $path 0]]
}
@@ -11011,6 +11027,7 @@ set mingaplen 100
set cmitmode "patch"
set wrapcomment "none"
set showneartags 1
+set hideremotes 0
set maxrefs 20
set maxlinelen 200
set showlocalchanges 1
diff --git a/gitweb/INSTALL b/gitweb/INSTALL
index 18c9ce3..b76a0cf 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/README b/gitweb/README
index 9056d1e..66c6a93 100644
--- a/gitweb/README
+++ b/gitweb/README
@@ -165,6 +165,12 @@ not include variables usually directly set during build):
Full URL and absolute URL of gitweb script;
in earlier versions of gitweb you might have need to set those
variables, now there should be no need to do it.
+ * $base_url
+ Base URL for relative URLs in pages generated by gitweb,
+ (e.g. $logo, $favicon, @stylesheets if they are relative URLs),
+ needed and used only for URLs with nonempty PATH_INFO via
+ <base href="$base_url>. Usually gitweb sets its value correctly,
+ and there is no need to set this variable, e.g. to $my_uri or "/".
* $home_link
Target of the home link on top of all pages (the first part of view
"breadcrumbs"). By default set to absolute URI of a page ($my_uri).
diff --git a/gitweb/git-favicon.png b/gitweb/git-favicon.png
index de637c0..aae35a7 100644
--- a/gitweb/git-favicon.png
+++ b/gitweb/git-favicon.png
Binary files differ
diff --git a/gitweb/git-logo.png b/gitweb/git-logo.png
index 16ae8d5..f4ede2e 100644
--- a/gitweb/git-logo.png
+++ b/gitweb/git-logo.png
Binary files differ
diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css
index d05bc37..8f68fe3 100644
--- a/gitweb/gitweb.css
+++ b/gitweb/gitweb.css
@@ -226,22 +226,30 @@ th {
text-align: left;
}
-tr.light:hover {
- background-color: #edece6;
-}
-
-tr.dark {
- background-color: #f6f6f0;
+/* do not change row style on hover for 'blame' view */
+tr.light,
+table.blame .light:hover {
+ background-color: #ffffff;
}
-tr.dark2 {
+tr.dark,
+table.blame .dark:hover {
background-color: #f6f6f0;
}
+/* currently both use the same, but it can change */
+tr.light:hover,
tr.dark:hover {
background-color: #edece6;
}
+/* boundary commits in 'blame' view */
+/* and commits without "previous" */
+tr.boundary td.sha1,
+tr.no-previous td.linenr {
+ font-weight: bold;
+}
+
td {
padding: 2px 5px;
font-size: 100%;
@@ -262,7 +270,7 @@ td.sha1 {
font-family: monospace;
}
-td.error {
+.error {
color: red;
background-color: yellow;
}
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 7fbd5ff..ff2d42a 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,
@@ -405,7 +415,7 @@ sub gitweb_get_feature {
@{$feature{$name}{'default'}});
if (!$override) { return @defaults; }
if (!defined $sub) {
- warn "feature $name is not overrideable";
+ warn "feature $name is not overridable";
return @defaults;
}
return $sub->(@defaults);
@@ -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++";
@@ -940,10 +951,13 @@ sub href {
if (defined $params{'hash_parent_base'}) {
$href .= esc_url($params{'hash_parent_base'});
# skip the file_parent if it's the same as the file_name
- delete $params{'file_parent'} if $params{'file_parent'} eq $params{'file_name'};
- if (defined $params{'file_parent'} && $params{'file_parent'} !~ /\.\./) {
- $href .= ":/".esc_url($params{'file_parent'});
- delete $params{'file_parent'};
+ if (defined $params{'file_parent'}) {
+ if (defined $params{'file_name'} && $params{'file_parent'} eq $params{'file_name'}) {
+ delete $params{'file_parent'};
+ } elsif ($params{'file_parent'} !~ /\.\./) {
+ $href .= ":/".esc_url($params{'file_parent'});
+ delete $params{'file_parent'};
+ }
}
$href .= "..";
delete $params{'hash_parent'};
@@ -1510,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;
}
}
@@ -2570,7 +2584,7 @@ sub parse_commit_text {
} elsif ((!defined $withparents) && ($line =~ m/^parent ([0-9a-fA-F]{40})$/)) {
push @parents, $1;
} elsif ($line =~ m/^author (.*) ([0-9]+) (.*)$/) {
- $co{'author'} = $1;
+ $co{'author'} = to_utf8($1);
$co{'author_epoch'} = $2;
$co{'author_tz'} = $3;
if ($co{'author'} =~ m/^([^<]+) <([^>]*)>/) {
@@ -2580,10 +2594,9 @@ sub parse_commit_text {
$co{'author_name'} = $co{'author'};
}
} elsif ($line =~ m/^committer (.*) ([0-9]+) (.*)$/) {
- $co{'committer'} = $1;
+ $co{'committer'} = to_utf8($1);
$co{'committer_epoch'} = $2;
$co{'committer_tz'} = $3;
- $co{'committer_name'} = $co{'committer'};
if ($co{'committer'} =~ m/^([^<]+) <([^>]*)>/) {
$co{'committer_name'} = $1;
$co{'committer_email'} = $2;
@@ -4801,7 +4814,7 @@ sub git_blame {
git_print_page_path($file_name, $ftype, $hash_base);
# page body
- my @rev_color = qw(light2 dark2);
+ my @rev_color = qw(light dark);
my $num_colors = scalar(@rev_color);
my $current_color = 0;
my %metainfo = ();
@@ -4819,15 +4832,18 @@ HTML
my ($full_rev, $orig_lineno, $lineno, $group_size) =
($line =~ /^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/);
if (!exists $metainfo{$full_rev}) {
- $metainfo{$full_rev} = {};
+ $metainfo{$full_rev} = { 'nprevious' => 0 };
}
my $meta = $metainfo{$full_rev};
my $data;
while ($data = <$fd>) {
chomp $data;
last if ($data =~ s/^\t//); # contents of line
- if ($data =~ /^(\S+) (.*)$/) {
- $meta->{$1} = $2;
+ if ($data =~ /^(\S+)(?: (.*))?$/) {
+ $meta->{$1} = $2 unless exists $meta->{$1};
+ }
+ if ($data =~ /^previous /) {
+ $meta->{'nprevious'}++;
}
}
my $short_rev = substr($full_rev, 0, 8);
@@ -4838,7 +4854,11 @@ HTML
if ($group_size) {
$current_color = ($current_color + 1) % $num_colors;
}
- print "<tr id=\"l$lineno\" class=\"$rev_color[$current_color]\">\n";
+ my $tr_class = $rev_color[$current_color];
+ $tr_class .= ' boundary' if (exists $meta->{'boundary'});
+ $tr_class .= ' no-previous' if ($meta->{'nprevious'} == 0);
+ $tr_class .= ' multiple-previous' if ($meta->{'nprevious'} > 1);
+ print "<tr id=\"l$lineno\" class=\"$tr_class\">\n";
if ($group_size) {
print "<td class=\"sha1\"";
print " title=\"". esc_html($author) . ", $date\"";
@@ -4848,22 +4868,31 @@ HTML
hash=>$full_rev,
file_name=>$file_name)},
esc_html($short_rev));
+ if ($group_size >= 2) {
+ my @author_initials = ($author =~ /\b([[:upper:]])\B/g);
+ if (@author_initials) {
+ print "<br />" .
+ esc_html(join('', @author_initials));
+ # or join('.', ...)
+ }
+ }
print "</td>\n";
}
- my $parent_commit;
- if (!exists $meta->{'parent'}) {
- open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
- or die_error(500, "Open git-rev-parse failed");
- $parent_commit = <$dd>;
- close $dd;
- chomp($parent_commit);
- $meta->{'parent'} = $parent_commit;
- } else {
- $parent_commit = $meta->{'parent'};
- }
+ # 'previous' <sha1 of parent commit> <filename at commit>
+ if (exists $meta->{'previous'} &&
+ $meta->{'previous'} =~ /^([a-fA-F0-9]{40}) (.*)$/) {
+ $meta->{'parent'} = $1;
+ $meta->{'file_parent'} = unquote($2);
+ }
+ my $linenr_commit =
+ exists($meta->{'parent'}) ?
+ $meta->{'parent'} : $full_rev;
+ my $linenr_filename =
+ exists($meta->{'file_parent'}) ?
+ $meta->{'file_parent'} : unquote($meta->{'filename'});
my $blamed = href(action => 'blame',
- file_name => $meta->{'filename'},
- hash_base => $parent_commit);
+ file_name => $linenr_filename,
+ hash_base => $linenr_commit);
print "<td class=\"linenr\">";
print $cgi->a({ -href => "$blamed#l$orig_lineno",
-class => "linenr" },
@@ -5163,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 e466770..6746d42 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
@@ -286,9 +291,10 @@ static int graph_is_interesting(struct git_graph *graph, struct commit *commit)
}
/*
- * Uninteresting and pruned commits won't be printed
+ * Otherwise, use get_commit_action() to see if this commit is
+ * interesting
*/
- return (commit->object.flags & (UNINTERESTING | TREESAME)) ? 0 : 1;
+ return get_commit_action(graph->revs, commit) == commit_show;
}
static struct commit_list *next_interesting_parent(struct git_graph *graph,
@@ -499,11 +505,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/grep.h b/grep.h
index f00db0e..28e6b2a 100644
--- a/grep.h
+++ b/grep.h
@@ -79,6 +79,7 @@ struct grep_opt {
int pathname;
int null_following_name;
int color;
+ int max_depth;
int funcname;
char color_match[COLOR_MAXLEN];
const char *color_external;
diff --git a/help.c b/help.c
index 6c46d8b..294337e 100644
--- a/help.c
+++ b/help.c
@@ -302,7 +302,7 @@ const char *help_unknown_cmd(const char *cmd)
struct cmdnames main_cmds, other_cmds;
memset(&main_cmds, 0, sizeof(main_cmds));
- memset(&other_cmds, 0, sizeof(main_cmds));
+ memset(&other_cmds, 0, sizeof(other_cmds));
memset(&aliases, 0, sizeof(aliases));
git_config(git_unknown_cmd_config, NULL);
@@ -334,7 +334,7 @@ const char *help_unknown_cmd(const char *cmd)
const char *assumed = main_cmds.names[0]->name;
main_cmds.names[0] = NULL;
clean_cmdnames(&main_cmds);
- fprintf(stderr, "WARNING: You called a Git program named '%s', "
+ fprintf(stderr, "WARNING: You called a Git command named '%s', "
"which does not exist.\n"
"Continuing under the assumption that you meant '%s'\n",
cmd, assumed);
diff --git a/http.c b/http.c
index a2720d5..5926c5b 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);
}
@@ -1004,7 +1006,6 @@ int finish_http_pack_request(struct http_pack_request *preq)
struct http_pack_request *new_http_pack_request(
struct packed_git *target, const char *base_url)
{
- char *url;
char *filename;
long prev_posn = 0;
char range[RANGE_HEADER_SIZE];
@@ -1018,8 +1019,7 @@ struct http_pack_request *new_http_pack_request(
end_url_with_slash(&buf, base_url);
strbuf_addf(&buf, "objects/pack/pack-%s.pack",
sha1_to_hex(target->sha1));
- url = strbuf_detach(&buf, NULL);
- preq->url = xstrdup(url);
+ preq->url = strbuf_detach(&buf, NULL);
filename = sha1_pack_name(target->sha1);
snprintf(preq->filename, sizeof(preq->filename), "%s", filename);
@@ -1035,7 +1035,7 @@ struct http_pack_request *new_http_pack_request(
preq->slot->local = preq->packfile;
curl_easy_setopt(preq->slot->curl, CURLOPT_FILE, preq->packfile);
curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
- curl_easy_setopt(preq->slot->curl, CURLOPT_URL, url);
+ curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
no_pragma_header);
@@ -1059,6 +1059,8 @@ struct http_pack_request *new_http_pack_request(
abort:
free(filename);
+ free(preq->url);
+ free(preq);
return NULL;
}
@@ -1098,7 +1100,6 @@ struct http_object_request *new_http_object_request(const char *base_url,
char *hex = sha1_to_hex(sha1);
char *filename;
char prevfile[PATH_MAX];
- char *url;
int prevlocal;
unsigned char prev_buf[PREV_BUF_SIZE];
ssize_t prev_read = 0;
@@ -1152,8 +1153,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
git_SHA1_Init(&freq->c);
- url = get_remote_object_url(base_url, hex, 0);
- freq->url = xstrdup(url);
+ freq->url = get_remote_object_url(base_url, hex, 0);
/*
* If a previous temp file is present, process what was already
@@ -1189,7 +1189,11 @@ struct http_object_request *new_http_object_request(const char *base_url,
if (prev_posn>0) {
prev_posn = 0;
lseek(freq->localfile, 0, SEEK_SET);
- ftruncate(freq->localfile, 0);
+ if (ftruncate(freq->localfile, 0) < 0) {
+ error("Couldn't truncate temporary file %s for %s: %s",
+ freq->tmpfile, freq->filename, strerror(errno));
+ goto abort;
+ }
}
}
@@ -1198,7 +1202,7 @@ struct http_object_request *new_http_object_request(const char *base_url,
curl_easy_setopt(freq->slot->curl, CURLOPT_FILE, freq);
curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
- curl_easy_setopt(freq->slot->curl, CURLOPT_URL, url);
+ curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
/*
@@ -1218,9 +1222,9 @@ struct http_object_request *new_http_object_request(const char *base_url,
return freq;
- free(url);
abort:
free(filename);
+ free(freq->url);
free(freq);
return NULL;
}
@@ -1285,5 +1289,10 @@ void release_http_object_request(struct http_object_request *freq)
free(freq->url);
freq->url = NULL;
}
- freq->slot = NULL;
+ if (freq->slot != NULL) {
+ freq->slot->callback_func = NULL;
+ freq->slot->callback_data = NULL;
+ release_active_slot(freq->slot);
+ freq->slot = NULL;
+ }
}
diff --git a/ll-merge.c b/ll-merge.c
index 0571564..2d6b6d6 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -192,10 +192,6 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
args[2] = cmd.buf;
status = run_command_v_opt(args, 0);
- if (status < -ERR_RUN_COMMAND_FORK)
- ; /* failure in run-command */
- else
- status = -status;
fd = open(temp[1], O_RDONLY);
if (fd < 0)
goto bad;
diff --git a/log-tree.c b/log-tree.c
index 6f73c17..1c9eefe 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);
}
}
@@ -168,18 +169,6 @@ static unsigned int digits_in_number(unsigned int number)
return result;
}
-static int has_non_ascii(const char *s)
-{
- int ch;
- if (!s)
- return 0;
- while ((ch = *s++) != '\0') {
- if (non_ascii(ch))
- return 1;
- }
- return 0;
-}
-
void get_patch_filename(struct commit *commit, int nr, const char *suffix,
struct strbuf *buf)
{
diff --git a/log-tree.h b/log-tree.h
index 20b5caf..3f7b400 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/merge-recursive.c b/merge-recursive.c
index d415c41..10d7913 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -952,9 +952,31 @@ static int process_renames(struct merge_options *o,
"%s added in %s",
ren1_src, ren1_dst, branch1,
ren1_dst, branch2);
- new_path = unique_path(o, ren1_dst, branch2);
- output(o, 1, "Adding as %s instead", new_path);
- update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
+ if (o->call_depth) {
+ struct merge_file_info mfi;
+ struct diff_filespec one, a, b;
+
+ one.path = a.path = b.path =
+ (char *)ren1_dst;
+ hashcpy(one.sha1, null_sha1);
+ one.mode = 0;
+ hashcpy(a.sha1, ren1->pair->two->sha1);
+ a.mode = ren1->pair->two->mode;
+ hashcpy(b.sha1, dst_other.sha1);
+ b.mode = dst_other.mode;
+ mfi = merge_file(o, &one, &a, &b,
+ branch1,
+ branch2);
+ output(o, 1, "Adding merged %s", ren1_dst);
+ update_file(o, 0,
+ mfi.sha,
+ mfi.mode,
+ ren1_dst);
+ } else {
+ new_path = unique_path(o, ren1_dst, branch2);
+ output(o, 1, "Adding as %s instead", new_path);
+ update_file(o, 0, dst_other.sha1, dst_other.mode, new_path);
+ }
} else if ((item = string_list_lookup(ren1_dst, renames2Dst))) {
ren2 = item->util;
clean_merge = 0;
diff --git a/mktag.c b/mktag.c
index a609e3e..a3b4270 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/mozilla-sha1/sha1.c b/mozilla-sha1/sha1.c
deleted file mode 100644
index 95a4ebf..0000000
--- a/mozilla-sha1/sha1.c
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code is SHA 180-1 Reference Implementation (Compact version)
- *
- * The Initial Developer of the Original Code is Paul Kocher of
- * Cryptography Research. Portions created by Paul Kocher are
- * Copyright (C) 1995-9 by Cryptography Research, Inc. All
- * Rights Reserved.
- *
- * Contributor(s):
- *
- * Paul Kocher
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable
- * instead of those above. If you wish to allow use of your
- * version of this file only under the terms of the GPL and not to
- * allow others to use your version of this file under the MPL,
- * indicate your decision by deleting the provisions above and
- * replace them with the notice and other provisions required by
- * the GPL. If you do not delete the provisions above, a recipient
- * may use your version of this file under either the MPL or the
- * GPL.
- */
-
-#include "sha1.h"
-
-static void shaHashBlock(moz_SHA_CTX *ctx);
-
-void moz_SHA1_Init(moz_SHA_CTX *ctx) {
- int i;
-
- ctx->lenW = 0;
- ctx->sizeHi = ctx->sizeLo = 0;
-
- /* Initialize H with the magic constants (see FIPS180 for constants)
- */
- ctx->H[0] = 0x67452301;
- ctx->H[1] = 0xefcdab89;
- ctx->H[2] = 0x98badcfe;
- ctx->H[3] = 0x10325476;
- ctx->H[4] = 0xc3d2e1f0;
-
- for (i = 0; i < 80; i++)
- ctx->W[i] = 0;
-}
-
-
-void moz_SHA1_Update(moz_SHA_CTX *ctx, const void *_dataIn, int len) {
- const unsigned char *dataIn = _dataIn;
- int i;
-
- /* Read the data into W and process blocks as they get full
- */
- for (i = 0; i < len; i++) {
- ctx->W[ctx->lenW / 4] <<= 8;
- ctx->W[ctx->lenW / 4] |= (unsigned int)dataIn[i];
- if ((++ctx->lenW) % 64 == 0) {
- shaHashBlock(ctx);
- ctx->lenW = 0;
- }
- ctx->sizeLo += 8;
- ctx->sizeHi += (ctx->sizeLo < 8);
- }
-}
-
-
-void moz_SHA1_Final(unsigned char hashout[20], moz_SHA_CTX *ctx) {
- unsigned char pad0x80 = 0x80;
- unsigned char pad0x00 = 0x00;
- unsigned char padlen[8];
- int i;
-
- /* Pad with a binary 1 (e.g. 0x80), then zeroes, then length
- */
- padlen[0] = (unsigned char)((ctx->sizeHi >> 24) & 255);
- padlen[1] = (unsigned char)((ctx->sizeHi >> 16) & 255);
- padlen[2] = (unsigned char)((ctx->sizeHi >> 8) & 255);
- padlen[3] = (unsigned char)((ctx->sizeHi >> 0) & 255);
- padlen[4] = (unsigned char)((ctx->sizeLo >> 24) & 255);
- padlen[5] = (unsigned char)((ctx->sizeLo >> 16) & 255);
- padlen[6] = (unsigned char)((ctx->sizeLo >> 8) & 255);
- padlen[7] = (unsigned char)((ctx->sizeLo >> 0) & 255);
- moz_SHA1_Update(ctx, &pad0x80, 1);
- while (ctx->lenW != 56)
- moz_SHA1_Update(ctx, &pad0x00, 1);
- moz_SHA1_Update(ctx, padlen, 8);
-
- /* Output hash
- */
- for (i = 0; i < 20; i++) {
- hashout[i] = (unsigned char)(ctx->H[i / 4] >> 24);
- ctx->H[i / 4] <<= 8;
- }
-
- /*
- * Re-initialize the context (also zeroizes contents)
- */
- moz_SHA1_Init(ctx);
-}
-
-
-#define SHA_ROT(X,n) (((X) << (n)) | ((X) >> (32-(n))))
-
-static void shaHashBlock(moz_SHA_CTX *ctx) {
- int t;
- unsigned int A,B,C,D,E,TEMP;
-
- for (t = 16; t <= 79; t++)
- ctx->W[t] =
- SHA_ROT(ctx->W[t-3] ^ ctx->W[t-8] ^ ctx->W[t-14] ^ ctx->W[t-16], 1);
-
- A = ctx->H[0];
- B = ctx->H[1];
- C = ctx->H[2];
- D = ctx->H[3];
- E = ctx->H[4];
-
- for (t = 0; t <= 19; t++) {
- TEMP = SHA_ROT(A,5) + (((C^D)&B)^D) + E + ctx->W[t] + 0x5a827999;
- E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
- }
- for (t = 20; t <= 39; t++) {
- TEMP = SHA_ROT(A,5) + (B^C^D) + E + ctx->W[t] + 0x6ed9eba1;
- E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
- }
- for (t = 40; t <= 59; t++) {
- TEMP = SHA_ROT(A,5) + ((B&C)|(D&(B|C))) + E + ctx->W[t] + 0x8f1bbcdc;
- E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
- }
- for (t = 60; t <= 79; t++) {
- TEMP = SHA_ROT(A,5) + (B^C^D) + E + ctx->W[t] + 0xca62c1d6;
- E = D; D = C; C = SHA_ROT(B, 30); B = A; A = TEMP;
- }
-
- ctx->H[0] += A;
- ctx->H[1] += B;
- ctx->H[2] += C;
- ctx->H[3] += D;
- ctx->H[4] += E;
-}
diff --git a/mozilla-sha1/sha1.h b/mozilla-sha1/sha1.h
deleted file mode 100644
index aa48a46..0000000
--- a/mozilla-sha1/sha1.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * The contents of this file are subject to the Mozilla Public
- * License Version 1.1 (the "License"); you may not use this file
- * except in compliance with the License. You may obtain a copy of
- * the License at http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
- * implied. See the License for the specific language governing
- * rights and limitations under the License.
- *
- * The Original Code is SHA 180-1 Header File
- *
- * The Initial Developer of the Original Code is Paul Kocher of
- * Cryptography Research. Portions created by Paul Kocher are
- * Copyright (C) 1995-9 by Cryptography Research, Inc. All
- * Rights Reserved.
- *
- * Contributor(s):
- *
- * Paul Kocher
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License Version 2 or later (the
- * "GPL"), in which case the provisions of the GPL are applicable
- * instead of those above. If you wish to allow use of your
- * version of this file only under the terms of the GPL and not to
- * allow others to use your version of this file under the MPL,
- * indicate your decision by deleting the provisions above and
- * replace them with the notice and other provisions required by
- * the GPL. If you do not delete the provisions above, a recipient
- * may use your version of this file under either the MPL or the
- * GPL.
- */
-
-typedef struct {
- unsigned int H[5];
- unsigned int W[80];
- int lenW;
- unsigned int sizeHi,sizeLo;
-} moz_SHA_CTX;
-
-void moz_SHA1_Init(moz_SHA_CTX *ctx);
-void moz_SHA1_Update(moz_SHA_CTX *ctx, const void *dataIn, int len);
-void moz_SHA1_Final(unsigned char hashout[20], moz_SHA_CTX *ctx);
-
-#define git_SHA_CTX moz_SHA_CTX
-#define git_SHA1_Init moz_SHA1_Init
-#define git_SHA1_Update moz_SHA1_Update
-#define git_SHA1_Final moz_SHA1_Final
diff --git a/object.c b/object.c
index a6ef439..fe8eaaf 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 3b71fbb..a64a4d6 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 b32587a..f295a2c 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 e5328da..f5983f8 100644
--- a/pretty.c
+++ b/pretty.c
@@ -86,6 +86,18 @@ int non_ascii(int ch)
return !isascii(ch) || ch == '\033';
}
+int has_non_ascii(const char *s)
+{
+ int ch;
+ if (!s)
+ return 0;
+ while ((ch = *s++) != '\0') {
+ if (non_ascii(ch))
+ return 1;
+ }
+ return 0;
+}
+
static int is_rfc2047_special(char ch)
{
return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
@@ -571,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 e49eaa3..24865cf 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,
@@ -821,7 +826,7 @@ static int remove_empty_directories(const char *file)
strbuf_init(&path, 20);
strbuf_addstr(&path, file);
- result = remove_dir_recursively(&path, 1);
+ result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
strbuf_release(&path);
diff --git a/refs.h b/refs.h
index c11f6a6..777b5b7 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 0000000..eb59604
--- /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 9f5dac5..35eca4a 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;
@@ -1664,7 +1664,7 @@ static inline int want_ancestry(struct rev_info *revs)
return (revs->rewrite_parents || revs->children.name);
}
-enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit)
{
if (commit->object.flags & SHOWN)
return commit_ignore;
@@ -1692,12 +1692,23 @@ enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
if (!commit->parents || !commit->parents->next)
return commit_ignore;
}
- if (want_ancestry(revs) && rewrite_parents(revs, commit) < 0)
- return commit_error;
}
return commit_show;
}
+enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit)
+{
+ enum commit_action action = get_commit_action(revs, commit);
+
+ if (action == commit_show &&
+ !revs->show_all &&
+ revs->prune && revs->dense && want_ancestry(revs)) {
+ if (rewrite_parents(revs, commit) < 0)
+ return commit_error;
+ }
+ return action;
+}
+
static struct commit *get_revision_1(struct rev_info *revs)
{
if (!revs->commits)
diff --git a/revision.h b/revision.h
index fb74492..9d0dddb 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;
@@ -165,6 +168,7 @@ enum commit_action {
commit_error
};
+extern enum commit_action get_commit_action(struct rev_info *revs, struct commit *commit);
extern enum commit_action simplify_commit(struct rev_info *revs, struct commit *commit);
#endif
diff --git a/run-command.c b/run-command.c
index ff3d8e2..f3e7abb 100644
--- a/run-command.c
+++ b/run-command.c
@@ -19,6 +19,7 @@ int start_command(struct child_process *cmd)
{
int need_in, need_out, need_err;
int fdin[2], fdout[2], fderr[2];
+ int failed_errno = failed_errno;
/*
* In case of errors we must keep the promise to close FDs
@@ -28,9 +29,10 @@ int start_command(struct child_process *cmd)
need_in = !cmd->no_stdin && cmd->in < 0;
if (need_in) {
if (pipe(fdin) < 0) {
+ failed_errno = errno;
if (cmd->out > 0)
close(cmd->out);
- return -ERR_RUN_COMMAND_PIPE;
+ goto fail_pipe;
}
cmd->in = fdin[1];
}
@@ -40,11 +42,12 @@ int start_command(struct child_process *cmd)
&& cmd->out < 0;
if (need_out) {
if (pipe(fdout) < 0) {
+ failed_errno = errno;
if (need_in)
close_pair(fdin);
else if (cmd->in)
close(cmd->in);
- return -ERR_RUN_COMMAND_PIPE;
+ goto fail_pipe;
}
cmd->out = fdout[0];
}
@@ -52,6 +55,7 @@ int start_command(struct child_process *cmd)
need_err = !cmd->no_stderr && cmd->err < 0;
if (need_err) {
if (pipe(fderr) < 0) {
+ failed_errno = errno;
if (need_in)
close_pair(fdin);
else if (cmd->in)
@@ -60,7 +64,11 @@ int start_command(struct child_process *cmd)
close_pair(fdout);
else if (cmd->out)
close(cmd->out);
- return -ERR_RUN_COMMAND_PIPE;
+fail_pipe:
+ error("cannot create pipe for %s: %s",
+ cmd->argv[0], strerror(failed_errno));
+ errno = failed_errno;
+ return -1;
}
cmd->err = fderr[0];
}
@@ -122,6 +130,9 @@ int start_command(struct child_process *cmd)
strerror(errno));
exit(127);
}
+ if (cmd->pid < 0)
+ error("cannot fork() for %s: %s", cmd->argv[0],
+ strerror(failed_errno = errno));
#else
int s0 = -1, s1 = -1, s2 = -1; /* backups of stdin, stdout, stderr */
const char **sargv = cmd->argv;
@@ -173,6 +184,9 @@ int start_command(struct child_process *cmd)
}
cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
+ failed_errno = errno;
+ if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
+ error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
if (cmd->env)
free_environ(env);
@@ -189,7 +203,6 @@ int start_command(struct child_process *cmd)
#endif
if (cmd->pid < 0) {
- int err = errno;
if (need_in)
close_pair(fdin);
else if (cmd->in)
@@ -200,9 +213,8 @@ int start_command(struct child_process *cmd)
close(cmd->out);
if (need_err)
close_pair(fderr);
- return err == ENOENT ?
- -ERR_RUN_COMMAND_EXEC :
- -ERR_RUN_COMMAND_FORK;
+ errno = failed_errno;
+ return -1;
}
if (need_in)
@@ -221,40 +233,51 @@ int start_command(struct child_process *cmd)
return 0;
}
-static int wait_or_whine(pid_t pid)
+static int wait_or_whine(pid_t pid, const char *argv0, int silent_exec_failure)
{
- for (;;) {
- int status, code;
- pid_t waiting = waitpid(pid, &status, 0);
-
- if (waiting < 0) {
- if (errno == EINTR)
- continue;
- error("waitpid failed (%s)", strerror(errno));
- return -ERR_RUN_COMMAND_WAITPID;
- }
- if (waiting != pid)
- return -ERR_RUN_COMMAND_WAITPID_WRONG_PID;
- if (WIFSIGNALED(status))
- return -ERR_RUN_COMMAND_WAITPID_SIGNAL;
-
- if (!WIFEXITED(status))
- return -ERR_RUN_COMMAND_WAITPID_NOEXIT;
+ int status, code = -1;
+ pid_t waiting;
+ int failed_errno = 0;
+
+ while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
+ ; /* nothing */
+
+ if (waiting < 0) {
+ failed_errno = errno;
+ error("waitpid for %s failed: %s", argv0, strerror(errno));
+ } else if (waiting != pid) {
+ error("waitpid is confused (%s)", argv0);
+ } else if (WIFSIGNALED(status)) {
+ code = WTERMSIG(status);
+ error("%s died of signal %d", argv0, code);
+ /*
+ * This return value is chosen so that code & 0xff
+ * mimics the exit code that a POSIX shell would report for
+ * a program that died from this signal.
+ */
+ code -= 128;
+ } else if (WIFEXITED(status)) {
code = WEXITSTATUS(status);
- switch (code) {
- case 127:
- return -ERR_RUN_COMMAND_EXEC;
- case 0:
- return 0;
- default:
- return -code;
+ /*
+ * Convert special exit code when execvp failed.
+ */
+ if (code == 127) {
+ code = -1;
+ failed_errno = ENOENT;
+ if (!silent_exec_failure)
+ error("cannot run %s: %s", argv0,
+ strerror(ENOENT));
}
+ } else {
+ error("waitpid is confused (%s)", argv0);
}
+ errno = failed_errno;
+ return code;
}
int finish_command(struct child_process *cmd)
{
- return wait_or_whine(cmd->pid);
+ return wait_or_whine(cmd->pid, cmd->argv[0], cmd->silent_exec_failure);
}
int run_command(struct child_process *cmd)
@@ -274,6 +297,7 @@ static void prepare_run_command_v_opt(struct child_process *cmd,
cmd->no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0;
cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
+ cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
}
int run_command_v_opt(const char **argv, int opt)
@@ -338,10 +362,7 @@ int start_async(struct async *async)
int finish_async(struct async *async)
{
#ifndef __MINGW32__
- int ret = 0;
-
- if (wait_or_whine(async->pid))
- ret = error("waitpid (async) failed");
+ int ret = wait_or_whine(async->pid, "child process", 0);
#else
DWORD ret = 0;
if (WaitForSingleObject(async->tid, INFINITE) != WAIT_OBJECT_0)
@@ -385,15 +406,7 @@ int run_hook(const char *index_file, const char *name, ...)
hook.env = env;
}
- ret = start_command(&hook);
+ ret = run_command(&hook);
free(argv);
- if (ret) {
- warning("Could not spawn %s", argv[0]);
- return ret;
- }
- ret = finish_command(&hook);
- if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
- warning("%s exited due to uncaught signal", argv[0]);
-
return ret;
}
diff --git a/run-command.h b/run-command.h
index e345502..0c00b25 100644
--- a/run-command.h
+++ b/run-command.h
@@ -1,17 +1,6 @@
#ifndef RUN_COMMAND_H
#define RUN_COMMAND_H
-enum {
- ERR_RUN_COMMAND_FORK = 10000,
- ERR_RUN_COMMAND_EXEC,
- ERR_RUN_COMMAND_PIPE,
- ERR_RUN_COMMAND_WAITPID,
- ERR_RUN_COMMAND_WAITPID_WRONG_PID,
- ERR_RUN_COMMAND_WAITPID_SIGNAL,
- ERR_RUN_COMMAND_WAITPID_NOEXIT,
-};
-#define IS_RUN_COMMAND_ERR(x) (-(x) >= ERR_RUN_COMMAND_FORK)
-
struct child_process {
const char **argv;
pid_t pid;
@@ -42,6 +31,7 @@ struct child_process {
unsigned no_stdout:1;
unsigned no_stderr:1;
unsigned git_cmd:1; /* if this is to be git sub-command */
+ unsigned silent_exec_failure:1;
unsigned stdout_to_stderr:1;
void (*preexec_cb)(void);
};
@@ -55,6 +45,7 @@ extern int run_hook(const char *index_file, const char *name, ...);
#define RUN_COMMAND_NO_STDIN 1
#define RUN_GIT_CMD 2 /*If this is to be git sub-command */
#define RUN_COMMAND_STDOUT_TO_STDERR 4
+#define RUN_SILENT_EXEC_FAILURE 8
int run_command_v_opt(const char **argv, int opt);
/*
diff --git a/send-pack.h b/send-pack.h
index 1d7b1b3..8b3cf02 100644
--- a/send-pack.h
+++ b/send-pack.h
@@ -3,6 +3,7 @@
struct send_pack_args {
unsigned verbose:1,
+ quiet:1,
send_mirror:1,
force_update:1,
use_thin_pack:1,
diff --git a/sha1_file.c b/sha1_file.c
index 1d996a1..4ea0b18 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 f03d117..a6153dc 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 eaa8704..d05e056 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/symlinks.c b/symlinks.c
index 4bdded3..7b0a86d 100644
--- a/symlinks.c
+++ b/symlinks.c
@@ -91,6 +91,10 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
longest_path_match(name, len, cache->path, cache->len,
&previous_slash);
match_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK);
+
+ if (!(track_flags & FL_FULLPATH) && match_len == len)
+ match_len = last_slash = previous_slash;
+
if (match_flags && match_len == cache->len)
return match_flags;
/*
diff --git a/t/Makefile b/t/Makefile
index bf816fc..bd09390 100644
--- a/t/Makefile
+++ b/t/Makefile
@@ -3,6 +3,8 @@
# Copyright (c) 2005 Junio C Hamano
#
+-include ../config.mak
+
#GIT_TEST_OPTS=--verbose --debug
SHELL_PATH ?= $(SHELL)
TAR ?= $(TAR)
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 5654962..fd8631f 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -14,7 +14,7 @@ if ! test_have_prereq PERL; then
fi
GIT_DIR=$PWD/.git
-GIT_SVN_DIR=$GIT_DIR/svn/git-svn
+GIT_SVN_DIR=$GIT_DIR/svn/refs/remotes/git-svn
SVN_TREE=$GIT_SVN_DIR/svn-tree
svn >/dev/null 2>&1
diff --git a/t/t0001-init.sh b/t/t0001-init.sh
index e3d8464..5386504 100755
--- a/t/t0001-init.sh
+++ b/t/t0001-init.sh
@@ -208,4 +208,87 @@ test_expect_success 'init rejects insanely long --template' '
)
'
+test_expect_success 'init creates a new directory' '
+ rm -fr newdir &&
+ (
+ git init newdir &&
+ test -d newdir/.git/refs
+ )
+'
+
+test_expect_success 'init creates a new bare directory' '
+ rm -fr newdir &&
+ (
+ git init --bare newdir &&
+ test -d newdir/refs
+ )
+'
+
+test_expect_success 'init recreates a directory' '
+ rm -fr newdir &&
+ (
+ mkdir newdir &&
+ git init newdir &&
+ test -d newdir/.git/refs
+ )
+'
+
+test_expect_success 'init recreates a new bare directory' '
+ rm -fr newdir &&
+ (
+ mkdir newdir &&
+ git init --bare newdir &&
+ test -d newdir/refs
+ )
+'
+
+test_expect_success 'init creates a new deep directory' '
+ rm -fr newdir &&
+ git init newdir/a/b/c &&
+ test -d newdir/a/b/c/.git/refs
+'
+
+test_expect_success POSIXPERM 'init creates a new deep directory (umask vs. shared)' '
+ rm -fr newdir &&
+ (
+ # Leading directories should honor umask while
+ # the repository itself should follow "shared"
+ umask 002 &&
+ git init --bare --shared=0660 newdir/a/b/c &&
+ test -d newdir/a/b/c/refs &&
+ ls -ld newdir/a newdir/a/b > lsab.out &&
+ ! grep -v "^drwxrw[sx]r-x" lsab.out &&
+ ls -ld newdir/a/b/c > lsc.out &&
+ ! grep -v "^drwxrw[sx]---" lsc.out
+ )
+'
+
+test_expect_success 'init notices EEXIST (1)' '
+ rm -fr newdir &&
+ (
+ >newdir &&
+ test_must_fail git init newdir &&
+ test -f newdir
+ )
+'
+
+test_expect_success 'init notices EEXIST (2)' '
+ rm -fr newdir &&
+ (
+ mkdir newdir &&
+ >newdir/a
+ test_must_fail git init newdir/a/b &&
+ test -f newdir/a
+ )
+'
+
+test_expect_success POSIXPERM 'init notices EPERM' '
+ rm -fr newdir &&
+ (
+ mkdir newdir &&
+ chmod -w newdir &&
+ test_must_fail git init newdir/a/b
+ )
+'
+
test_done
diff --git a/t/t1009-read-tree-new-index.sh b/t/t1009-read-tree-new-index.sh
new file mode 100755
index 0000000..59b3aa4
--- /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/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 8c43dcd..83b7294 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -755,6 +755,11 @@ echo >>result
test_expect_success '--null --get-regexp' 'cmp result expect'
+test_expect_success 'inner whitespace kept verbatim' '
+ git config section.val "foo bar" &&
+ test "z$(git config section.val)" = "zfoo bar"
+'
+
test_expect_success SYMLINKS 'symlinked configuration' '
ln -s notyet myconfig &&
diff --git a/t/t2000-checkout-cache-clash.sh b/t/t2000-checkout-cache-clash.sh
index f7e1a73..de3edb5 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 c5c29cc..4e6a44b 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/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index a973628..4cae019 100755
--- a/t/t3404-rebase-interactive.sh
+++ b/t/t3404-rebase-interactive.sh
@@ -10,7 +10,7 @@ that the result still makes sense.
'
. ./test-lib.sh
-. ../lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
set_fake_editor
diff --git a/t/t3409-rebase-preserve-merges.sh b/t/t3409-rebase-preserve-merges.sh
index e6c8327..297d165 100755
--- a/t/t3409-rebase-preserve-merges.sh
+++ b/t/t3409-rebase-preserve-merges.sh
@@ -71,7 +71,7 @@ test_expect_success 'rebase -p fakes interactive rebase' '
git fetch &&
git rebase -p origin/topic &&
test 1 = $(git rev-list --all --pretty=oneline | grep "Modify A" | wc -l) &&
- test 1 = $(git rev-list --all --pretty=oneline | grep "Merge commit" | wc -l)
+ test 1 = $(git rev-list --all --pretty=oneline | grep "Merge remote branch " | wc -l)
)
'
diff --git a/t/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh
index 6533505..14a23cd 100755
--- a/t/t3411-rebase-preserve-around-merges.sh
+++ b/t/t3411-rebase-preserve-around-merges.sh
@@ -10,7 +10,7 @@ a merge to before the merge.
'
. ./test-lib.sh
-. ../lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
set_fake_editor
diff --git a/t/t3414-rebase-preserve-onto.sh b/t/t3414-rebase-preserve-onto.sh
index 80019ee..ee0a6cc 100755
--- a/t/t3414-rebase-preserve-onto.sh
+++ b/t/t3414-rebase-preserve-onto.sh
@@ -10,7 +10,7 @@ aren'"'"'t on top of $ONTO, even if they are on top of $UPSTREAM.
'
. ./test-lib.sh
-. ../lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
# Set up branches like this:
# A1---B1---E1---F1---G1
diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh
index fd2a55a..62fd65e 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 8b33321..8e3694e 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 0000000..903d9d9
--- /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/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 922a894..531f5b7 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -493,13 +493,12 @@ test_expect_success 'format-patch from a subdirectory (2)' '
'
test_expect_success 'format-patch from a subdirectory (3)' '
- here="$TEST_DIRECTORY/$test" &&
rm -f 0* &&
filename=$(
rm -rf sub &&
mkdir -p sub/dir &&
cd sub/dir &&
- git format-patch -1 -o "$here"
+ git format-patch -1 -o "$TRASH_DIRECTORY"
) &&
basename=$(expr "$filename" : ".*/\(.*\)") &&
test -f "$basename"
diff --git a/t/t4020-diff-external.sh b/t/t4020-diff-external.sh
index 4ea42e0..a7602cf 100755
--- a/t/t4020-diff-external.sh
+++ b/t/t4020-diff-external.sh
@@ -166,7 +166,7 @@ test_expect_success 'diff --cached' '
git update-index --assume-unchanged file &&
echo second >file &&
git diff --cached >actual &&
- test_cmp ../t4020/diff.NUL actual
+ test_cmp "$TEST_DIRECTORY"/t4020/diff.NUL actual
'
test_done
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
new file mode 100755
index 0000000..9d9498b
--- /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 0000000..484654d
--- /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/t4132-apply-removal.sh b/t/t4132-apply-removal.sh
new file mode 100755
index 0000000..bb1ffe3
--- /dev/null
+++ b/t/t4132-apply-removal.sh
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Junio C Hamano
+
+test_description='git-apply notices removal patches generated by GNU diff'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+ cat <<-EOF >c &&
+ diff -ruN a/file b/file
+ --- a/file TS0
+ +++ b/file TS1
+ @@ -0,0 +1 @@
+ +something
+ EOF
+
+ cat <<-EOF >d &&
+ diff -ruN a/file b/file
+ --- a/file TS0
+ +++ b/file TS1
+ @@ -1 +0,0 @@
+ -something
+ EOF
+
+ timeWest="1982-09-16 07:00:00.000000000 -0800" &&
+ timeGMT="1982-09-16 15:00:00.000000000 +0000" &&
+ timeEast="1982-09-17 00:00:00.000000000 +0900" &&
+
+ epocWest="1969-12-31 16:00:00.000000000 -0800" &&
+ epocGMT="1970-01-01 00:00:00.000000000 +0000" &&
+ epocEast="1970-01-01 09:00:00.000000000 +0900" &&
+
+ sed -e "s/TS0/$epocWest/" -e "s/TS1/$timeWest/" <c >createWest.patch &&
+ sed -e "s/TS0/$epocEast/" -e "s/TS1/$timeEast/" <c >createEast.patch &&
+ sed -e "s/TS0/$epocGMT/" -e "s/TS1/$timeGMT/" <c >createGMT.patch &&
+
+ sed -e "s/TS0/$timeWest/" -e "s/TS1/$timeWest/" <c >addWest.patch &&
+ sed -e "s/TS0/$timeEast/" -e "s/TS1/$timeEast/" <c >addEast.patch &&
+ sed -e "s/TS0/$timeGMT/" -e "s/TS1/$timeGMT/" <c >addGMT.patch &&
+
+ sed -e "s/TS0/$timeWest/" -e "s/TS1/$timeWest/" <d >emptyWest.patch &&
+ sed -e "s/TS0/$timeEast/" -e "s/TS1/$timeEast/" <d >emptyEast.patch &&
+ sed -e "s/TS0/$timeGMT/" -e "s/TS1/$timeGMT/" <d >emptyGMT.patch &&
+
+ sed -e "s/TS0/$timeWest/" -e "s/TS1/$epocWest/" <d >removeWest.patch &&
+ sed -e "s/TS0/$timeEast/" -e "s/TS1/$epocEast/" <d >removeEast.patch &&
+ sed -e "s/TS0/$timeGMT/" -e "s/TS1/$epocGMT/" <d >removeGMT.patch &&
+
+ echo something >something &&
+ >empty
+'
+
+for patch in *.patch
+do
+ test_expect_success "test $patch" '
+ rm -f file .git/index &&
+ case "$patch" in
+ create*)
+ # must be able to create
+ git apply --index $patch &&
+ test_cmp file something &&
+ # must notice the file is already there
+ >file &&
+ git add file &&
+ test_must_fail git apply $patch
+ ;;
+ add*)
+ # must be able to create or patch
+ git apply $patch &&
+ test_cmp file something &&
+ >file &&
+ git apply $patch &&
+ test_cmp file something
+ ;;
+ empty*)
+ # must leave an empty file
+ cat something >file &&
+ git add file &&
+ git apply --index $patch &&
+ test -f file &&
+ test_cmp empty file
+ ;;
+ remove*)
+ # must remove the file
+ cat something >file &&
+ git add file &&
+ git apply --index $patch &&
+ ! test -f file
+ ;;
+ esac
+ '
+done
+
+test_done
diff --git a/t/t4150-am.sh b/t/t4150-am.sh
index a12bf84..8296605 100755
--- a/t/t4150-am.sh
+++ b/t/t4150-am.sh
@@ -77,6 +77,12 @@ test_expect_success setup '
git commit -s -F msg &&
git tag second &&
git format-patch --stdout first >patch1 &&
+ {
+ echo "X-Fake-Field: Line One" &&
+ echo "X-Fake-Field: Line Two" &&
+ echo "X-Fake-Field: Line Three" &&
+ git format-patch --stdout first | sed -e "1d"
+ } > patch1.eml &&
sed -n -e "3,\$p" msg >file &&
git add file &&
test_tick &&
@@ -108,6 +114,15 @@ test_expect_success 'am applies patch correctly' '
test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
'
+test_expect_success 'am applies patch e-mail not in a mbox' '
+ git checkout first &&
+ git am patch1.eml &&
+ ! test -d .git/rebase-apply &&
+ test -z "$(git diff second)" &&
+ test "$(git rev-parse second)" = "$(git rev-parse HEAD)" &&
+ test "$(git rev-parse second^)" = "$(git rev-parse HEAD^)"
+'
+
GIT_AUTHOR_NAME="Another Thor"
GIT_AUTHOR_EMAIL="a.thor@example.com"
GIT_COMMITTER_NAME="Co M Miter"
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 48e0088..1e952ca 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -320,11 +320,11 @@ test_expect_success 'set up more tangled history' '
'
cat > expect <<\EOF
-* Merge branch 'reach'
+* Merge commit 'reach'
|\
| \
| \
-*-. \ Merge branches 'octopus-a' and 'octopus-b'
+*-. \ Merge commit 'octopus-a'; commit 'octopus-b'
|\ \ \
* | | | seventh
| | * | octopus-b
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index bee3424..d13c806 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -9,6 +9,11 @@ test_description='Per branch config variables affects "git fetch".
D=`pwd`
+test_bundle_object_count () {
+ git verify-pack -v "$1" >verify.out &&
+ test "$2" = $(grep '^[0-9a-f]\{40\} ' verify.out | wc -l)
+}
+
test_expect_success setup '
echo >file original &&
git add file &&
@@ -146,6 +151,7 @@ test_expect_success 'unbundle 1' '
test_must_fail git fetch "$D/bundle1" master:master
'
+
test_expect_success 'bundle 1 has only 3 files ' '
cd "$D" &&
(
@@ -156,8 +162,7 @@ test_expect_success 'bundle 1 has only 3 files ' '
cat
) <bundle1 >bundle.pack &&
git index-pack bundle.pack &&
- verify=$(git verify-pack -v bundle.pack) &&
- test 4 = $(echo "$verify" | wc -l)
+ test_bundle_object_count bundle.pack 3
'
test_expect_success 'unbundle 2' '
@@ -180,7 +185,7 @@ test_expect_success 'bundle does not prerequisite objects' '
cat
) <bundle3 >bundle.pack &&
git index-pack bundle.pack &&
- test 4 = $(git verify-pack -v bundle.pack | wc -l)
+ test_bundle_object_count bundle.pack 3
'
test_expect_success 'bundle should be able to create a full history' '
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index c5a2e66..dd2ee84 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -117,6 +117,19 @@ test_expect_success '--rebase with rebased default upstream' '
'
+test_expect_success 'rebased upstream + fetch + pull --rebase' '
+
+ git update-ref refs/remotes/me/copy copy-orig &&
+ git reset --hard to-rebase-orig &&
+ git checkout --track -b to-rebase3 me/copy &&
+ git reset --hard to-rebase-orig &&
+ git fetch &&
+ git pull --rebase &&
+ test "conflicting modification" = "$(cat file)" &&
+ test file = "$(cat file2)"
+
+'
+
test_expect_success 'pull --rebase dies early with dirty working directory' '
git checkout to-rebase &&
@@ -136,4 +149,15 @@ test_expect_success 'pull --rebase dies early with dirty working directory' '
'
+test_expect_success 'pull --rebase works on branch yet to be born' '
+ git rev-parse master >expect &&
+ mkdir empty_repo &&
+ (cd empty_repo &&
+ git init &&
+ git pull --rebase .. master &&
+ git rev-parse HEAD >../actual
+ ) &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5530-upload-pack-error.sh b/t/t5530-upload-pack-error.sh
index f5102b9..a696b87 100755
--- a/t/t5530-upload-pack-error.sh
+++ b/t/t5530-upload-pack-error.sh
@@ -30,11 +30,12 @@ test_expect_success 'fsck fails' '
test_must_fail git fsck
'
-test_expect_success 'upload-pack fails due to error in pack-objects' '
+test_expect_success 'upload-pack fails due to error in pack-objects packing' '
! echo "0032want $(git rev-parse HEAD)
00000009done
0000" | git upload-pack . > /dev/null 2> output.err &&
+ grep "unable to read" output.err &&
grep "pack-objects died" output.err
'
@@ -51,9 +52,21 @@ test_expect_success 'fsck fails' '
test_expect_success 'upload-pack fails due to error in rev-list' '
! echo "0032want $(git rev-parse HEAD)
+0034shallow $(git rev-parse HEAD^)00000009done
+0000" | git upload-pack . > /dev/null 2> output.err &&
+ # pack-objects survived
+ grep "Total.*, reused" output.err &&
+ # but there was an error, which must have been in rev-list
+ grep "bad tree object" output.err
+'
+
+test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
+
+ ! echo "0032want $(git rev-parse HEAD)
00000009done
0000" | git upload-pack . > /dev/null 2> output.err &&
- grep "waitpid (async) failed" output.err
+ grep "bad tree object" output.err &&
+ grep "pack-objects died" output.err
'
test_expect_success 'create empty repository' '
diff --git a/t/t5531-deep-submodule-push.sh b/t/t5531-deep-submodule-push.sh
new file mode 100644
index 0000000..65d8d47
--- /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/t6010-merge-base.sh b/t/t6010-merge-base.sh
index 04e4b7c..0144d9e 100755
--- a/t/t6010-merge-base.sh
+++ b/t/t6010-merge-base.sh
@@ -110,6 +110,18 @@ test_expect_success 'compute merge-base (all)' \
# Another set to demonstrate base between one commit and a merge
# in the documentation.
+#
+# * C (MMC) * B (MMB) * A (MMA)
+# * o * o * o
+# * o * o * o
+# * o * o * o
+# * o | _______/
+# | |/
+# | * 1 (MM1)
+# | _______/
+# |/
+# * root (MMR)
+
test_expect_success 'merge-base for octopus-step (setup)' '
test_tick && git commit --allow-empty -m root && git tag MMR &&
@@ -137,6 +149,12 @@ test_expect_success 'merge-base A B C' '
test "$MM1" = "$MB"
'
+test_expect_success 'merge-base A B C using show-branch' '
+ MB=$(git show-branch --merge-base MMA MMB MMC) &&
+ MMR=$(git rev-parse --verify MMR) &&
+ test "$MMR" = "$MB"
+'
+
test_expect_success 'criss-cross merge-base for octopus-step (setup)' '
git reset --hard MMR &&
test_tick && git commit --allow-empty -m 1 && git tag CC1 &&
diff --git a/t/t6015-rev-list-show-all-parents.sh b/t/t6015-rev-list-show-all-parents.sh
new file mode 100755
index 0000000..8b146fb
--- /dev/null
+++ b/t/t6015-rev-list-show-all-parents.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+test_description='--show-all --parents does not rewrite TREESAME commits'
+
+. ./test-lib.sh
+
+test_expect_success 'set up --show-all --parents test' '
+ test_commit one foo.txt &&
+ commit1=`git rev-list -1 HEAD` &&
+ test_commit two bar.txt &&
+ commit2=`git rev-list -1 HEAD` &&
+ test_commit three foo.txt &&
+ commit3=`git rev-list -1 HEAD`
+ '
+
+test_expect_success '--parents rewrites TREESAME parents correctly' '
+ echo $commit3 $commit1 > expected &&
+ echo $commit1 >> expected &&
+ git rev-list --parents HEAD -- foo.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--parents --show-all does not rewrites TREESAME parents' '
+ echo $commit3 $commit2 > expected &&
+ echo $commit2 $commit1 >> expected &&
+ echo $commit1 >> expected &&
+ git rev-list --parents --show-all HEAD -- foo.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_done
diff --git a/t/t6016-rev-list-graph-simplify-history.sh b/t/t6016-rev-list-graph-simplify-history.sh
new file mode 100755
index 0000000..27fd52b
--- /dev/null
+++ b/t/t6016-rev-list-graph-simplify-history.sh
@@ -0,0 +1,276 @@
+#!/bin/sh
+
+# There's more than one "correct" way to represent the history graphically.
+# These tests depend on the current behavior of the graphing code. If the
+# graphing code is ever changed to draw the output differently, these tests
+# cases will need to be updated to know about the new layout.
+
+test_description='--graph and simplified history'
+
+. ./test-lib.sh
+
+test_expect_success 'set up rev-list --graph test' '
+ # 3 commits on branch A
+ test_commit A1 foo.txt &&
+ test_commit A2 bar.txt &&
+ test_commit A3 bar.txt &&
+ git branch -m master A &&
+
+ # 2 commits on branch B, started from A1
+ git checkout -b B A1 &&
+ test_commit B1 foo.txt &&
+ test_commit B2 abc.txt &&
+
+ # 2 commits on branch C, started from A2
+ git checkout -b C A2 &&
+ test_commit C1 xyz.txt &&
+ test_commit C2 xyz.txt &&
+
+ # Octopus merge B and C into branch A
+ git checkout A &&
+ git merge B C &&
+ git tag A4
+
+ test_commit A5 bar.txt &&
+
+ # More commits on C, then merge C into A
+ git checkout C &&
+ test_commit C3 foo.txt &&
+ test_commit C4 bar.txt &&
+ git checkout A &&
+ git merge -s ours C &&
+ git tag A6
+
+ test_commit A7 bar.txt &&
+
+ # Store commit names in variables for later use
+ A1=$(git rev-parse --verify A1) &&
+ A2=$(git rev-parse --verify A2) &&
+ A3=$(git rev-parse --verify A3) &&
+ A4=$(git rev-parse --verify A4) &&
+ A5=$(git rev-parse --verify A5) &&
+ A6=$(git rev-parse --verify A6) &&
+ A7=$(git rev-parse --verify A7) &&
+ B1=$(git rev-parse --verify B1) &&
+ B2=$(git rev-parse --verify B2) &&
+ C1=$(git rev-parse --verify C1) &&
+ C2=$(git rev-parse --verify C2) &&
+ C3=$(git rev-parse --verify C3) &&
+ C4=$(git rev-parse --verify C4)
+ '
+
+test_expect_success '--graph --all' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "| | " >> expected &&
+ echo "| \\ " >> expected &&
+ echo "*-. \\ $A4" >> expected &&
+ echo "|\\ \\ \\ " >> expected &&
+ echo "| | |/ " >> expected &&
+ echo "| | * $C2" >> expected &&
+ echo "| | * $C1" >> expected &&
+ echo "| * | $B2" >> expected &&
+ echo "| * | $B1" >> expected &&
+ echo "* | | $A3" >> expected &&
+ echo "| |/ " >> expected &&
+ echo "|/| " >> expected &&
+ echo "* | $A2" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --all > actual &&
+ test_cmp expected actual
+ '
+
+# Make sure the graph_is_interesting() code still realizes
+# that undecorated merges are interesting, even with --simplify-by-decoration
+test_expect_success '--graph --simplify-by-decoration' '
+ rm -f expected &&
+ git tag -d A4
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "| | " >> expected &&
+ echo "| \\ " >> expected &&
+ echo "*-. \\ $A4" >> expected &&
+ echo "|\\ \\ \\ " >> expected &&
+ echo "| | |/ " >> expected &&
+ echo "| | * $C2" >> expected &&
+ echo "| | * $C1" >> expected &&
+ echo "| * | $B2" >> expected &&
+ echo "| * | $B1" >> expected &&
+ echo "* | | $A3" >> expected &&
+ echo "| |/ " >> expected &&
+ echo "|/| " >> expected &&
+ echo "* | $A2" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --all --simplify-by-decoration > actual &&
+ test_cmp expected actual
+ '
+
+# Get rid of all decorations on branch B, and graph with it simplified away
+test_expect_success '--graph --simplify-by-decoration prune branch B' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "* | $A4" >> expected &&
+ echo "|\\ \\ " >> expected &&
+ echo "| |/ " >> expected &&
+ echo "| * $C2" >> expected &&
+ echo "| * $C1" >> expected &&
+ echo "* | $A3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --simplify-by-decoration --all > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph --full-history -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "* | $A4" >> expected &&
+ echo "|\\ \\ " >> expected &&
+ echo "| |/ " >> expected &&
+ echo "* | $A3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ git rev-list --graph --full-history --all -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph --full-history --simplify-merges -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "* | $A3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ git rev-list --graph --full-history --simplify-merges --all \
+ -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A3" >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ git rev-list --graph --all -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph --sparse -- bar.txt' '
+ rm -f expected &&
+ git tag -d B2
+ git tag -d B1
+ git branch -d B
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A4" >> expected &&
+ echo "* $A3" >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "| * $C3" >> expected &&
+ echo "| * $C2" >> expected &&
+ echo "| * $C1" >> expected &&
+ echo "|/ " >> expected &&
+ echo "* $A2" >> expected &&
+ echo "* $A1" >> expected &&
+ git rev-list --graph --sparse --all -- bar.txt > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph ^C4' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A4" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $B2" >> expected &&
+ echo "| * $B1" >> expected &&
+ echo "* $A3" >> expected &&
+ git rev-list --graph --all ^C4 > actual &&
+ test_cmp expected actual
+ '
+
+test_expect_success '--graph ^C3' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* $A5" >> expected &&
+ echo "* $A4" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $B2" >> expected &&
+ echo "| * $B1" >> expected &&
+ echo "* $A3" >> expected &&
+ git rev-list --graph --all ^C3 > actual &&
+ test_cmp expected actual
+ '
+
+# I don't think the ordering of the boundary commits is really
+# that important, but this test depends on it. If the ordering ever changes
+# in the code, we'll need to update this test.
+test_expect_success '--graph --boundary ^C3' '
+ rm -f expected &&
+ echo "* $A7" >> expected &&
+ echo "* $A6" >> expected &&
+ echo "|\\ " >> expected &&
+ echo "| * $C4" >> expected &&
+ echo "* | $A5" >> expected &&
+ echo "| | " >> expected &&
+ echo "| \\ " >> expected &&
+ echo "*-. \\ $A4" >> expected &&
+ echo "|\\ \\ \\ " >> expected &&
+ echo "| * | | $B2" >> expected &&
+ echo "| * | | $B1" >> expected &&
+ echo "* | | | $A3" >> expected &&
+ echo "o | | | $A2" >> expected &&
+ echo "|/ / / " >> expected &&
+ echo "o | | $A1" >> expected &&
+ echo " / / " >> expected &&
+ echo "| o $C3" >> expected &&
+ echo "|/ " >> expected &&
+ echo "o $C2" >> expected &&
+ git rev-list --graph --boundary --all ^C3 > actual &&
+ test_cmp expected actual
+ '
+
+test_done
diff --git a/t/t6030-bisect-porcelain.sh b/t/t6030-bisect-porcelain.sh
index 1315bab..def397c 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/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh
new file mode 100755
index 0000000..5b96fb0
--- /dev/null
+++ b/t/t6035-merge-dir-to-symlink.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='merging when a directory was replaced with a symlink'
+. ./test-lib.sh
+
+if ! test_have_prereq SYMLINKS
+then
+ say 'Symbolic links not supported, skipping tests.'
+ test_done
+fi
+
+test_expect_success 'create a commit where dir a/b changed to symlink' '
+ mkdir -p a/b/c a/b-2/c &&
+ > a/b/c/d &&
+ > a/b-2/c/d &&
+ > a/x &&
+ git add -A &&
+ git commit -m base &&
+ git tag start &&
+ rm -rf a/b &&
+ ln -s b-2 a/b &&
+ git add -A &&
+ git commit -m "dir to symlink"
+'
+
+test_expect_success 'keep a/b-2/c/d across checkout' '
+ git checkout HEAD^0 &&
+ git reset --hard master &&
+ git rm --cached a/b &&
+ git commit -m "untracked symlink remains" &&
+ git checkout start^0 &&
+ test -f a/b-2/c/d
+'
+
+test_expect_success 'checkout should not have deleted a/b-2/c/d' '
+ git checkout HEAD^0 &&
+ git reset --hard master &&
+ git checkout start^0 &&
+ test -f a/b-2/c/d
+'
+
+test_expect_success 'setup for merge test' '
+ git reset --hard &&
+ test -f a/b-2/c/d &&
+ echo x > a/x &&
+ git add a/x &&
+ git commit -m x &&
+ git tag baseline
+'
+
+test_expect_success 'do not lose a/b-2/c/d in merge (resolve)' '
+ git reset --hard &&
+ git checkout baseline^0 &&
+ git merge -s resolve master &&
+ test -h a/b &&
+ test -f a/b-2/c/d
+'
+
+test_expect_failure 'do not lose a/b-2/c/d in merge (recursive)' '
+ git reset --hard &&
+ git checkout baseline^0 &&
+ git merge -s recursive master &&
+ test -h a/b &&
+ test -f a/b-2/c/d
+'
+
+test_expect_success 'setup a merge where dir a/b-2 changed to symlink' '
+ git reset --hard &&
+ git checkout start^0 &&
+ rm -rf a/b-2 &&
+ ln -s b a/b-2 &&
+ git add -A &&
+ git commit -m "dir a/b-2 to symlink" &&
+ git tag test2
+'
+
+test_expect_failure 'merge should not have conflicts (resolve)' '
+ git reset --hard &&
+ git checkout baseline^0 &&
+ git merge -s resolve test2 &&
+ test -h a/b-2 &&
+ test -f a/b/c/d
+'
+
+test_expect_failure 'merge should not have conflicts (recursive)' '
+ git reset --hard &&
+ git checkout baseline^0 &&
+ git merge -s recursive test2 &&
+ test -h a/b-2 &&
+ test -f a/b/c/d
+'
+
+test_done
diff --git a/t/t6036-recursive-corner-cases.sh b/t/t6036-recursive-corner-cases.sh
new file mode 100755
index 0000000..b874141
--- /dev/null
+++ b/t/t6036-recursive-corner-cases.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+test_description='recursive merge corner cases'
+
+. ./test-lib.sh
+
+#
+# L1 L2
+# o---o
+# / \ / \
+# o X ?
+# \ / \ /
+# o---o
+# R1 R2
+#
+
+test_expect_success setup '
+ ten="0 1 2 3 4 5 6 7 8 9"
+ for i in $ten
+ do
+ echo line $i in a sample file
+ done >one &&
+ for i in $ten
+ do
+ echo line $i in another sample file
+ done >two &&
+ git add one two &&
+ test_tick && git commit -m initial &&
+
+ git branch L1 &&
+ git checkout -b R1 &&
+ git mv one three &&
+ test_tick && git commit -m R1 &&
+
+ git checkout L1 &&
+ git mv two three &&
+ test_tick && git commit -m L1 &&
+
+ git checkout L1^0 &&
+ test_tick && git merge -s ours R1 &&
+ git tag L2 &&
+
+ git checkout R1^0 &&
+ test_tick && git merge -s ours L1 &&
+ git tag R2
+'
+
+test_expect_success merge '
+ git reset --hard &&
+ git checkout L2^0 &&
+
+ test_must_fail git merge -s recursive R2^0
+'
+
+test_done
diff --git a/t/t6050-replace.sh b/t/t6050-replace.sh
new file mode 100755
index 0000000..8b8bd81
--- /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/t7002-grep.sh b/t/t7002-grep.sh
index b13aa7e..b4709e2 100755
--- a/t/t7002-grep.sh
+++ b/t/t7002-grep.sh
@@ -25,13 +25,17 @@ test_expect_success setup '
echo foo mmap bar_mmap
echo foo_mmap bar mmap baz
} >file &&
+ echo vvv >v &&
echo ww w >w &&
echo x x xx x >x &&
echo y yy >y &&
echo zzz > z &&
mkdir t &&
echo test >t/t &&
- git add file w x y z t/t hello.c &&
+ echo vvv >t/v &&
+ mkdir t/a &&
+ echo vvv >t/a/v &&
+ git add . &&
test_tick &&
git commit -m initial
'
@@ -132,6 +136,51 @@ do
! git grep -c test $H | grep /dev/null
'
+ test_expect_success "grep --max-depth -1 $L" '
+ {
+ echo ${HC}t/a/v:1:vvv
+ echo ${HC}t/v:1:vvv
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth -1 -n -e vvv $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 0 $L" '
+ {
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth 0 -n -e vvv $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 0 -- '*' $L" '
+ {
+ echo ${HC}t/a/v:1:vvv
+ echo ${HC}t/v:1:vvv
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth 0 -n -e vvv $H -- "*" >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 1 $L" '
+ {
+ echo ${HC}t/v:1:vvv
+ echo ${HC}v:1:vvv
+ } >expected &&
+ git grep --max-depth 1 -n -e vvv $H >actual &&
+ test_cmp expected actual
+ '
+
+ test_expect_success "grep --max-depth 0 -- t $L" '
+ {
+ echo ${HC}t/v:1:vvv
+ } >expected &&
+ git grep --max-depth 0 -n -e vvv $H -- t >actual &&
+ test_cmp expected actual
+ '
+
done
cat >expected <<EOF
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index 929d5d4..118c6eb 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -380,4 +380,43 @@ test_expect_success 'removal failure' '
'
chmod 755 foo
+test_expect_success 'nested git work tree' '
+ rm -fr foo bar &&
+ mkdir foo bar &&
+ (
+ cd foo &&
+ git init &&
+ >hello.world
+ git add . &&
+ git commit -a -m nested
+ ) &&
+ (
+ cd bar &&
+ >goodbye.people
+ ) &&
+ git clean -f -d &&
+ test -f foo/.git/index &&
+ test -f foo/hello.world &&
+ ! test -d bar
+'
+
+test_expect_success 'force removal of nested git work tree' '
+ rm -fr foo bar &&
+ mkdir foo bar &&
+ (
+ cd foo &&
+ git init &&
+ >hello.world
+ git add . &&
+ git commit -a -m nested
+ ) &&
+ (
+ cd bar &&
+ >goodbye.people
+ ) &&
+ git clean -f -f -d &&
+ ! test -d foo &&
+ ! test -d bar
+'
+
test_done
diff --git a/t/t7401-submodule-summary.sh b/t/t7401-submodule-summary.sh
index 6149829..6cc16c3 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/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
new file mode 100755
index 0000000..2a52775
--- /dev/null
+++ b/t/t7407-submodule-foreach.sh
@@ -0,0 +1,237 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Johan Herland
+#
+
+test_description='Test "git submodule foreach"
+
+This test verifies that "git submodule foreach" correctly visits all submodules
+that are currently checked out.
+'
+
+. ./test-lib.sh
+
+
+test_expect_success 'setup a submodule tree' '
+ echo file > file &&
+ git add file &&
+ test_tick &&
+ git commit -m upstream
+ git clone . super &&
+ git clone super submodule &&
+ (
+ cd super &&
+ git submodule add ../submodule sub1 &&
+ git submodule add ../submodule sub2 &&
+ git submodule add ../submodule sub3 &&
+ git config -f .gitmodules --rename-section \
+ submodule.sub1 submodule.foo1 &&
+ git config -f .gitmodules --rename-section \
+ submodule.sub2 submodule.foo2 &&
+ git config -f .gitmodules --rename-section \
+ submodule.sub3 submodule.foo3 &&
+ git add .gitmodules
+ test_tick &&
+ git commit -m "submodules" &&
+ git submodule init sub1 &&
+ git submodule init sub2 &&
+ git submodule init sub3
+ ) &&
+ (
+ cd submodule &&
+ echo different > file &&
+ git add file &&
+ test_tick &&
+ git commit -m "different"
+ ) &&
+ (
+ cd super &&
+ (
+ cd sub3 &&
+ git pull
+ ) &&
+ git add sub3 &&
+ test_tick &&
+ git commit -m "update sub3"
+ )
+'
+
+sub1sha1=$(cd super/sub1 && git rev-parse HEAD)
+sub3sha1=$(cd super/sub3 && git rev-parse HEAD)
+
+cat > expect <<EOF
+Entering 'sub1'
+foo1-sub1-$sub1sha1
+Entering 'sub3'
+foo3-sub3-$sub3sha1
+EOF
+
+test_expect_success 'test basic "submodule foreach" usage' '
+ git clone super clone &&
+ (
+ cd clone &&
+ git submodule update --init -- sub1 sub3 &&
+ git submodule foreach "echo \$name-\$path-\$sha1" > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup nested submodules' '
+ git clone submodule nested1 &&
+ git clone submodule nested2 &&
+ git clone submodule nested3 &&
+ (
+ cd nested3 &&
+ git submodule add ../submodule submodule &&
+ test_tick &&
+ git commit -m "submodule" &&
+ git submodule init submodule
+ ) &&
+ (
+ cd nested2 &&
+ git submodule add ../nested3 nested3 &&
+ test_tick &&
+ git commit -m "nested3" &&
+ git submodule init nested3
+ ) &&
+ (
+ cd nested1 &&
+ git submodule add ../nested2 nested2 &&
+ test_tick &&
+ git commit -m "nested2" &&
+ git submodule init nested2
+ ) &&
+ (
+ cd super &&
+ git submodule add ../nested1 nested1 &&
+ test_tick &&
+ git commit -m "nested1" &&
+ git submodule init nested1
+ )
+'
+
+test_expect_success 'use "submodule foreach" to checkout 2nd level submodule' '
+ git clone super clone2 &&
+ (
+ cd clone2 &&
+ test ! -d sub1/.git &&
+ test ! -d sub2/.git &&
+ test ! -d sub3/.git &&
+ test ! -d nested1/.git &&
+ git submodule update --init &&
+ test -d sub1/.git &&
+ test -d sub2/.git &&
+ test -d sub3/.git &&
+ test -d nested1/.git &&
+ test ! -d nested1/nested2/.git &&
+ git submodule foreach "git submodule update --init" &&
+ test -d nested1/nested2/.git &&
+ test ! -d nested1/nested2/nested3/.git
+ )
+'
+
+test_expect_success 'use "foreach --recursive" to checkout all submodules' '
+ (
+ cd clone2 &&
+ git submodule foreach --recursive "git submodule update --init" &&
+ test -d nested1/nested2/nested3/.git &&
+ test -d nested1/nested2/nested3/submodule/.git
+ )
+'
+
+cat > expect <<EOF
+Entering 'nested1'
+Entering 'nested1/nested2'
+Entering 'nested1/nested2/nested3'
+Entering 'nested1/nested2/nested3/submodule'
+Entering 'sub1'
+Entering 'sub2'
+Entering 'sub3'
+EOF
+
+test_expect_success 'test messages from "foreach --recursive"' '
+ (
+ cd clone2 &&
+ git submodule foreach --recursive "true" > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+cat > expect <<EOF
+nested1-nested1
+nested2-nested2
+nested3-nested3
+submodule-submodule
+foo1-sub1
+foo2-sub2
+foo3-sub3
+EOF
+
+test_expect_success 'test "foreach --quiet --recursive"' '
+ (
+ cd clone2 &&
+ git submodule foreach -q --recursive "echo \$name-\$path" > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'use "update --recursive" to checkout all submodules' '
+ git clone super clone3 &&
+ (
+ cd clone3 &&
+ test ! -d sub1/.git &&
+ test ! -d sub2/.git &&
+ test ! -d sub3/.git &&
+ test ! -d nested1/.git &&
+ git submodule update --init --recursive &&
+ test -d sub1/.git &&
+ test -d sub2/.git &&
+ test -d sub3/.git &&
+ test -d nested1/.git &&
+ test -d nested1/nested2/.git &&
+ test -d nested1/nested2/nested3/.git &&
+ test -d nested1/nested2/nested3/submodule/.git
+ )
+'
+
+nested1sha1=$(cd clone3/nested1 && git rev-parse HEAD)
+nested2sha1=$(cd clone3/nested1/nested2 && git rev-parse HEAD)
+nested3sha1=$(cd clone3/nested1/nested2/nested3 && git rev-parse HEAD)
+submodulesha1=$(cd clone3/nested1/nested2/nested3/submodule && git rev-parse HEAD)
+sub1sha1=$(cd clone3/sub1 && git rev-parse HEAD)
+sub2sha1=$(cd clone3/sub2 && git rev-parse HEAD)
+sub3sha1=$(cd clone3/sub3 && git rev-parse HEAD)
+sub1sha1_short=$(cd clone3/sub1 && git rev-parse --short HEAD)
+sub2sha1_short=$(cd clone3/sub2 && git rev-parse --short HEAD)
+
+cat > expect <<EOF
+ $nested1sha1 nested1 (heads/master)
+ $nested2sha1 nested1/nested2 (heads/master)
+ $nested3sha1 nested1/nested2/nested3 (heads/master)
+ $submodulesha1 nested1/nested2/nested3/submodule (heads/master)
+ $sub1sha1 sub1 ($sub1sha1_short)
+ $sub2sha1 sub2 ($sub2sha1_short)
+ $sub3sha1 sub3 (heads/master)
+EOF
+
+test_expect_success 'test "status --recursive"' '
+ (
+ cd clone3 &&
+ git submodule status --recursive > ../actual
+ ) &&
+ test_cmp expect actual
+'
+
+test_expect_success 'use "git clone --recursive" to checkout all submodules' '
+ git clone --recursive super clone4 &&
+ test -d clone4/.git &&
+ test -d clone4/sub1/.git &&
+ test -d clone4/sub2/.git &&
+ test -d clone4/sub3/.git &&
+ test -d clone4/nested1/.git &&
+ test -d clone4/nested1/nested2/.git &&
+ test -d clone4/nested1/nested2/nested3/.git &&
+ test -d clone4/nested1/nested2/nested3/submodule/.git
+'
+
+test_done
diff --git a/t/t7406-submodule-reference.sh b/t/t7408-submodule-reference.sh
index cc16d3f..cc16d3f 100755
--- a/t/t7406-submodule-reference.sh
+++ b/t/t7408-submodule-reference.sh
diff --git a/t/t7608-merge-messages.sh b/t/t7608-merge-messages.sh
new file mode 100755
index 0000000..28d5679
--- /dev/null
+++ b/t/t7608-merge-messages.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+
+test_description='test auto-generated merge messages'
+. ./test-lib.sh
+
+check_oneline() {
+ echo "$1" | sed "s/Q/'/g" >expect &&
+ git log -1 --pretty=tformat:%s >actual &&
+ test_cmp expect actual
+}
+
+test_expect_success 'merge local branch' '
+ test_commit master-1 &&
+ git checkout -b local-branch &&
+ test_commit branch-1 &&
+ git checkout master &&
+ test_commit master-2 &&
+ git merge local-branch &&
+ check_oneline "Merge branch Qlocal-branchQ"
+'
+
+test_expect_success 'merge octopus branches' '
+ git checkout -b octopus-a master &&
+ test_commit octopus-1 &&
+ git checkout -b octopus-b master &&
+ test_commit octopus-2 &&
+ git checkout master &&
+ git merge octopus-a octopus-b &&
+ check_oneline "Merge branches Qoctopus-aQ and Qoctopus-bQ"
+'
+
+test_expect_success 'merge tag' '
+ git checkout -b tag-branch master &&
+ test_commit tag-1 &&
+ git checkout master &&
+ test_commit master-3 &&
+ git merge tag-1 &&
+ check_oneline "Merge commit Qtag-1Q"
+'
+
+test_expect_success 'ambiguous tag' '
+ git checkout -b ambiguous master &&
+ test_commit ambiguous &&
+ git checkout master &&
+ test_commit master-4 &&
+ git merge ambiguous &&
+ check_oneline "Merge commit QambiguousQ"
+'
+
+test_expect_success 'remote branch' '
+ git checkout -b remote master &&
+ test_commit remote-1 &&
+ git update-ref refs/remotes/origin/master remote &&
+ git checkout master &&
+ test_commit master-5 &&
+ git merge origin/master &&
+ check_oneline "Merge remote branch Qorigin/masterQ"
+'
+
+test_done
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 9da4178..929499e 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -142,7 +142,9 @@ test_expect_success 'test show-ignore' "
touch deeply/nested/directory/.keep &&
svn_cmd add deeply &&
svn_cmd up &&
- svn_cmd propset -R svn:ignore 'no-such-file*' .
+ svn_cmd propset -R svn:ignore '
+no-such-file*
+' .
svn_cmd commit -m 'propset svn:ignore'
cd .. &&
git svn show-ignore > show-ignore.got &&
@@ -171,6 +173,7 @@ test_expect_success 'test create-ignore' "
"
cat >prop.expect <<\EOF
+
no-such-file*
EOF
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index 78610b6..bbfd7f4 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -172,11 +172,11 @@ test_expect_success "follow-parent is atomic" '
git update-ref refs/remotes/flunk@18 refs/remotes/stunk~2 &&
git update-ref -d refs/remotes/stunk &&
git config --unset svn-remote.svn.fetch stunk &&
- mkdir -p "$GIT_DIR"/svn/flunk@18 &&
- rev_map=$(cd "$GIT_DIR"/svn/stunk && ls .rev_map*) &&
- dd if="$GIT_DIR"/svn/stunk/$rev_map \
- of="$GIT_DIR"/svn/flunk@18/$rev_map bs=24 count=1 &&
- rm -rf "$GIT_DIR"/svn/stunk &&
+ mkdir -p "$GIT_DIR"/svn/refs/remotes/flunk@18 &&
+ rev_map=$(cd "$GIT_DIR"/svn/refs/remotes/stunk && ls .rev_map*) &&
+ dd if="$GIT_DIR"/svn/refs/remotes/stunk/$rev_map \
+ of="$GIT_DIR"/svn/refs/remotes/flunk@18/$rev_map bs=24 count=1 &&
+ rm -rf "$GIT_DIR"/svn/refs/remotes/stunk &&
git svn init --minimize-url -i flunk "$svnrepo"/flunk &&
git svn fetch -i flunk &&
git svn init --minimize-url -i stunk "$svnrepo"/stunk &&
diff --git a/t/t9107-git-svn-migrate.sh b/t/t9107-git-svn-migrate.sh
index 3a9e077..901b8e0 100755
--- a/t/t9107-git-svn-migrate.sh
+++ b/t/t9107-git-svn-migrate.sh
@@ -16,9 +16,7 @@ test_expect_success 'setup old-looking metadata' '
cd .. &&
git svn init "$svnrepo" &&
git svn fetch &&
- mv "$GIT_DIR"/svn/* "$GIT_DIR"/ &&
- mv "$GIT_DIR"/svn/.metadata "$GIT_DIR"/ &&
- rmdir "$GIT_DIR"/svn &&
+ rm -rf "$GIT_DIR"/svn &&
git update-ref refs/heads/git-svn-HEAD refs/${remotes_git_svn} &&
git update-ref refs/heads/svn-HEAD refs/${remotes_git_svn} &&
git update-ref -d refs/${remotes_git_svn} refs/${remotes_git_svn}
@@ -56,7 +54,15 @@ test_expect_success 'initialize a multi-repository repo' '
git config --add svn-remote.svn.fetch "branches/b:refs/remotes/b" &&
for i in tags/0.1 tags/0.2 tags/0.3; do
git config --add svn-remote.svn.fetch \
- $i:refs/remotes/$i || exit 1; done
+ $i:refs/remotes/$i || exit 1; done &&
+ git config --get-all svn-remote.svn.fetch > fetch.out &&
+ grep "^trunk:refs/remotes/trunk$" fetch.out &&
+ grep "^branches/a:refs/remotes/a$" fetch.out &&
+ grep "^branches/b:refs/remotes/b$" fetch.out &&
+ grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
+ grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
+ grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out &&
+ grep "^:refs/${remotes_git_svn}" fetch.out
'
# refs should all be different, but the trees should all be the same:
@@ -79,36 +85,36 @@ test_expect_success 'migrate --minimize on old inited layout' '
rm -rf "$GIT_DIR"/svn &&
for i in `cat fetch.out`; do
path=`expr $i : "\([^:]*\):.*$"`
- ref=`expr $i : "[^:]*:refs/remotes/\(.*\)$"`
+ ref=`expr $i : "[^:]*:\(refs/remotes/.*\)$"`
if test -z "$ref"; then continue; fi
if test -n "$path"; then path="/$path"; fi
( mkdir -p "$GIT_DIR"/svn/$ref/info/ &&
echo "$svnrepo"$path > "$GIT_DIR"/svn/$ref/info/url ) || exit 1;
done &&
git svn migrate --minimize &&
- test -z "`git config -l |grep -v "^svn-remote\.git-svn\."`" &&
+ test -z "`git config -l | grep "^svn-remote\.git-svn\."`" &&
git config --get-all svn-remote.svn.fetch > fetch.out &&
grep "^trunk:refs/remotes/trunk$" fetch.out &&
grep "^branches/a:refs/remotes/a$" fetch.out &&
grep "^branches/b:refs/remotes/b$" fetch.out &&
grep "^tags/0\.1:refs/remotes/tags/0\.1$" fetch.out &&
grep "^tags/0\.2:refs/remotes/tags/0\.2$" fetch.out &&
- grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out
+ grep "^tags/0\.3:refs/remotes/tags/0\.3$" fetch.out &&
grep "^:refs/${remotes_git_svn}" fetch.out
'
test_expect_success ".rev_db auto-converted to .rev_map.UUID" '
git svn fetch -i trunk &&
- test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
- expect="$(ls "$GIT_DIR"/svn/trunk/.rev_map.*)" &&
+ test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" &&
+ expect="$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_map.*)" &&
test -n "$expect" &&
rev_db="$(echo $expect | sed -e "s,_map,_db,")" &&
convert_to_rev_db "$expect" "$rev_db" &&
rm -f "$expect" &&
test -f "$rev_db" &&
git svn fetch -i trunk &&
- test -z "$(ls "$GIT_DIR"/svn/trunk/.rev_db.* 2>/dev/null)" &&
- test ! -e "$GIT_DIR"/svn/trunk/.rev_db &&
+ test -z "$(ls "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db.* 2>/dev/null)" &&
+ test ! -e "$GIT_DIR"/svn/refs/remotes/trunk/.rev_db &&
test -f "$expect"
'
diff --git a/t/t9120-git-svn-clone-with-percent-escapes.sh b/t/t9120-git-svn-clone-with-percent-escapes.sh
index f159ab6..9d9ebd5 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/t/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh
index 03705fa..5280e5f 100755
--- a/t/t9135-git-svn-moved-branch-empty-file.sh
+++ b/t/t9135-git-svn-moved-branch-empty-file.sh
@@ -10,7 +10,12 @@ test_expect_success 'load svn dumpfile' '
test_expect_success 'clone using git svn' 'git svn clone -s "$svnrepo" x'
test_expect_success 'test that b1 exists and is empty' '
- (cd x && test -f b1 && ! test -s b1)
+ (
+ cd x &&
+ git reset --hard branch-c &&
+ test -f b1 &&
+ ! test -s b1
+ )
'
test_done
diff --git a/t/t9143-git-svn-gc.sh b/t/t9143-git-svn-gc.sh
index f2ba2d1..99f69c6 100755
--- a/t/t9143-git-svn-gc.sh
+++ b/t/t9143-git-svn-gc.sh
@@ -28,26 +28,26 @@ test_expect_success 'Setup repo' 'git svn init "$svnrepo"'
test_expect_success 'Fetch repo' 'git svn fetch'
test_expect_success 'make backup copy of unhandled.log' '
- cp .git/svn/git-svn/unhandled.log tmp
+ cp .git/svn/refs/remotes/git-svn/unhandled.log tmp
'
-test_expect_success 'create leftover index' '> .git/svn/git-svn/index'
+test_expect_success 'create leftover index' '> .git/svn/refs/remotes/git-svn/index'
test_expect_success 'git svn gc runs' 'git svn gc'
-test_expect_success 'git svn index removed' '! test -f .git/svn/git-svn/index'
+test_expect_success 'git svn index removed' '! test -f .git/svn/refs/remotes/git-svn/index'
if perl -MCompress::Zlib -e 0 2>/dev/null
then
test_expect_success 'git svn gc produces a valid gzip file' '
- gunzip .git/svn/git-svn/unhandled.log.gz
+ gunzip .git/svn/refs/remotes/git-svn/unhandled.log.gz
'
else
say "Perl Compress::Zlib unavailable, skipping gunzip test"
fi
test_expect_success 'git svn gc does not change unhandled.log files' '
- test_cmp .git/svn/git-svn/unhandled.log tmp/unhandled.log
+ test_cmp .git/svn/refs/remotes/git-svn/unhandled.log tmp/unhandled.log
'
test_done
diff --git a/t/t9144-git-svn-old-rev_map.sh b/t/t9144-git-svn-old-rev_map.sh
new file mode 100755
index 0000000..7600a35
--- /dev/null
+++ b/t/t9144-git-svn-old-rev_map.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+
+test_description='git svn old rev_map preservd'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository with old layout' '
+ mkdir i &&
+ (cd i && > a) &&
+ svn_cmd import -m- i "$svnrepo" &&
+ git svn init "$svnrepo" &&
+ git svn fetch &&
+ test -d .git/svn/refs/remotes/git-svn/ &&
+ ! test -e .git/svn/git-svn/ &&
+ mv .git/svn/refs/remotes/git-svn .git/svn/ &&
+ rm -r .git/svn/refs
+'
+
+test_expect_success 'old layout continues to work' '
+ svn_cmd import -m- i "$svnrepo/b" &&
+ git svn rebase &&
+ echo a >> b/a &&
+ git add b/a &&
+ git commit -m- -a &&
+ git svn dcommit &&
+ ! test -d .git/svn/refs/ &&
+ test -e .git/svn/git-svn/
+'
+
+test_done
diff --git a/t/t9145-git-svn-master-branch.sh b/t/t9145-git-svn-master-branch.sh
new file mode 100755
index 0000000..16852d2
--- /dev/null
+++ b/t/t9145-git-svn-master-branch.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+#
+test_description='git svn initial master branch is "trunk" if possible'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+ mkdir i &&
+ > i/a &&
+ svn_cmd import -m trunk i "$svnrepo/trunk" &&
+ svn_cmd import -m b/a i "$svnrepo/branches/a" &&
+ svn_cmd import -m b/b i "$svnrepo/branches/b"
+'
+
+test_expect_success 'git svn clone --stdlayout sets up trunk as master' '
+ git svn clone -s "$svnrepo" g &&
+ (
+ cd g &&
+ test x`git rev-parse --verify refs/remotes/trunk^0` = \
+ x`git rev-parse --verify refs/heads/master^0`
+ )
+'
+
+test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 5fdc5d9..f2ca536 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -114,6 +114,9 @@ do
valgrind=t; verbose=t; shift ;;
--tee)
shift ;; # was handled already
+ --root=*)
+ root=$(expr "z$1" : 'z[^=]*=\(.*\)')
+ shift ;;
*)
echo "error: unknown test option '$1'" >&2; exit 1 ;;
esac
@@ -645,7 +648,12 @@ fi
# Test repository
test="trash directory.$(basename "$0" .sh)"
-test ! -z "$debug" || remove_trash="$TEST_DIRECTORY/$test"
+test -n "$root" && test="$root/$test"
+case "$test" in
+/*) TRASH_DIRECTORY="$test" ;;
+ *) TRASH_DIRECTORY="$TEST_DIRECTORY/$test" ;;
+esac
+test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
rm -fr "$test" || {
GIT_EXIT_OK=t
echo >&5 "FATAL: Cannot prepare test area"
@@ -677,6 +685,21 @@ do
esac
done
+# Provide an implementation of the 'yes' utility
+yes () {
+ if test $# = 0
+ then
+ y=y
+ else
+ y="$*"
+ fi
+
+ while echo "$y"
+ do
+ :
+ done
+}
+
# Fix some commands on Windows
case $(uname -s) in
*MINGW*)
diff --git a/transport.c b/transport.c
index de0d587..faee154 100644
--- a/transport.c
+++ b/transport.c
@@ -396,7 +396,6 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
{
const char **argv;
int argc;
- int err;
if (flags & TRANSPORT_PUSH_MIRROR)
return error("http transport does not support mirror mode");
@@ -416,20 +415,7 @@ static int curl_transport_push(struct transport *transport, int refspec_nr, cons
while (refspec_nr--)
argv[argc++] = *refspec++;
argv[argc] = NULL;
- err = run_command_v_opt(argv, RUN_GIT_CMD);
- switch (err) {
- case -ERR_RUN_COMMAND_FORK:
- error("unable to fork for %s", argv[0]);
- case -ERR_RUN_COMMAND_EXEC:
- error("unable to exec %s", argv[0]);
- break;
- case -ERR_RUN_COMMAND_WAITPID:
- case -ERR_RUN_COMMAND_WAITPID_WRONG_PID:
- case -ERR_RUN_COMMAND_WAITPID_SIGNAL:
- case -ERR_RUN_COMMAND_WAITPID_NOEXIT:
- error("%s died with strange error", argv[0]);
- }
- return !!err;
+ return !!run_command_v_opt(argv, RUN_GIT_CMD);
}
static struct ref *get_refs_via_curl(struct transport *transport, int for_push)
@@ -681,6 +667,21 @@ static int fetch_refs_via_pack(struct transport *transport,
return (refs ? 0 : -1);
}
+static int push_had_errors(struct ref *ref)
+{
+ for (; ref; ref = ref->next) {
+ switch (ref->status) {
+ case REF_STATUS_NONE:
+ case REF_STATUS_UPTODATE:
+ case REF_STATUS_OK:
+ break;
+ default:
+ return 1;
+ }
+ }
+ return 0;
+}
+
static int refs_pushed(struct ref *ref)
{
for (; ref; ref = ref->next) {
@@ -820,7 +821,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count, i
}
static void print_push_status(const char *dest, struct ref *refs,
- int verbose, int porcelain)
+ int verbose, int porcelain, int * nonfastforward)
{
struct ref *ref;
int n = 0;
@@ -835,11 +836,14 @@ static void print_push_status(const char *dest, struct ref *refs,
if (ref->status == REF_STATUS_OK)
n += print_one_push_status(ref, dest, n, porcelain);
+ *nonfastforward = 0;
for (ref = refs; ref; ref = ref->next) {
if (ref->status != REF_STATUS_NONE &&
ref->status != REF_STATUS_UPTODATE &&
ref->status != REF_STATUS_OK)
n += print_one_push_status(ref, dest, n, porcelain);
+ if (ref->status == REF_STATUS_REJECT_NONFASTFORWARD)
+ *nonfastforward = 1;
}
}
@@ -892,6 +896,7 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
args.use_thin_pack = data->thin;
args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
+ args.quiet = !!(flags & TRANSPORT_PUSH_QUIET);
args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
ret = send_pack(&args, data->fd, data->conn, remote_refs,
@@ -997,7 +1002,8 @@ int transport_set_option(struct transport *transport,
}
int transport_push(struct transport *transport,
- int refspec_nr, const char **refspec, int flags)
+ int refspec_nr, const char **refspec, int flags,
+ int * nonfastforward)
{
verify_remote_names(refspec_nr, refspec);
@@ -1009,6 +1015,7 @@ int transport_push(struct transport *transport,
struct ref *local_refs = get_local_heads();
int match_flags = MATCH_REFS_NONE;
int verbose = flags & TRANSPORT_PUSH_VERBOSE;
+ int quiet = flags & TRANSPORT_PUSH_QUIET;
int porcelain = flags & TRANSPORT_PUSH_PORCELAIN;
int ret;
@@ -1024,7 +1031,10 @@ int transport_push(struct transport *transport,
ret = transport->push_refs(transport, remote_refs, flags);
- print_push_status(transport->url, remote_refs, verbose | porcelain, porcelain);
+ if (!quiet || push_had_errors(remote_refs))
+ print_push_status(transport->url, remote_refs,
+ verbose | porcelain, porcelain,
+ nonfastforward);
if (!(flags & TRANSPORT_PUSH_DRY_RUN)) {
struct ref *ref;
diff --git a/transport.h b/transport.h
index 51b5397..171a01c 100644
--- a/transport.h
+++ b/transport.h
@@ -36,6 +36,7 @@ struct transport {
#define TRANSPORT_PUSH_MIRROR 8
#define TRANSPORT_PUSH_VERBOSE 16
#define TRANSPORT_PUSH_PORCELAIN 32
+#define TRANSPORT_PUSH_QUIET 64
/* Returns a transport suitable for the url */
struct transport *transport_get(struct remote *, const char *);
@@ -68,7 +69,8 @@ int transport_set_option(struct transport *transport, const char *name,
const char *value);
int transport_push(struct transport *connection,
- int refspec_nr, const char **refspec, int flags);
+ int refspec_nr, const char **refspec, int flags,
+ int * nonfastforward);
const struct ref *transport_get_remote_refs(struct transport *transport);
diff --git a/unpack-trees.h b/unpack-trees.h
index 1e0e232..d19df44 100644
--- a/unpack-trees.h
+++ b/unpack-trees.h
@@ -17,18 +17,18 @@ struct unpack_trees_error_msgs {
};
struct unpack_trees_options {
- unsigned int reset:1,
- merge:1,
- update:1,
- index_only:1,
- nontrivial_merge:1,
- trivial_merges_only:1,
- verbose_update:1,
- aggressive:1,
- skip_unmerged:1,
- initial_checkout:1,
- diff_index_cached:1,
- gently:1;
+ unsigned int reset,
+ merge,
+ update,
+ index_only,
+ nontrivial_merge,
+ trivial_merges_only,
+ verbose_update,
+ aggressive,
+ skip_unmerged,
+ initial_checkout,
+ diff_index_cached,
+ gently;
const char *prefix;
int pos;
struct dir_struct *dir;
diff --git a/upload-pack.c b/upload-pack.c
index 841ebb5..4d8be83 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -29,6 +29,7 @@ static unsigned long oldest_have;
static int multi_ack, nr_our_refs;
static int use_thin_pack, use_ofs_delta, use_include_tag;
static int no_progress, daemon_mode;
+static int shallow_nr;
static struct object_array have_obj;
static struct object_array want_obj;
static unsigned int timeout;
@@ -107,8 +108,6 @@ static int do_rev_list(int fd, void *create_full_pack)
struct rev_info revs;
pack_pipe = fdopen(fd, "w");
- if (create_full_pack)
- use_thin_pack = 0; /* no point doing it */
init_revisions(&revs, NULL);
revs.tag_objects = 1;
revs.tree_objects = 1;
@@ -155,13 +154,21 @@ static void create_pack_file(void)
const char *argv[10];
int arg = 0;
- rev_list.proc = do_rev_list;
- /* .data is just a boolean: any non-NULL value will do */
- rev_list.data = create_full_pack ? &rev_list : NULL;
- if (start_async(&rev_list))
- die("git upload-pack: unable to fork git-rev-list");
+ if (shallow_nr) {
+ rev_list.proc = do_rev_list;
+ rev_list.data = 0;
+ if (start_async(&rev_list))
+ die("git upload-pack: unable to fork git-rev-list");
+ argv[arg++] = "pack-objects";
+ } else {
+ argv[arg++] = "pack-objects";
+ argv[arg++] = "--revs";
+ if (create_full_pack)
+ argv[arg++] = "--all";
+ else if (use_thin_pack)
+ argv[arg++] = "--thin";
+ }
- argv[arg++] = "pack-objects";
argv[arg++] = "--stdout";
if (!no_progress)
argv[arg++] = "--progress";
@@ -172,7 +179,7 @@ static void create_pack_file(void)
argv[arg++] = NULL;
memset(&pack_objects, 0, sizeof(pack_objects));
- pack_objects.in = rev_list.out; /* start_command closes it */
+ pack_objects.in = shallow_nr ? rev_list.out : -1;
pack_objects.out = -1;
pack_objects.err = -1;
pack_objects.git_cmd = 1;
@@ -181,6 +188,24 @@ static void create_pack_file(void)
if (start_command(&pack_objects))
die("git upload-pack: unable to fork git-pack-objects");
+ /* pass on revisions we (don't) want */
+ if (!shallow_nr) {
+ FILE *pipe_fd = fdopen(pack_objects.in, "w");
+ if (!create_full_pack) {
+ int i;
+ for (i = 0; i < want_obj.nr; i++)
+ fprintf(pipe_fd, "%s\n", sha1_to_hex(want_obj.objects[i].item->sha1));
+ fprintf(pipe_fd, "--not\n");
+ for (i = 0; i < have_obj.nr; i++)
+ fprintf(pipe_fd, "%s\n", sha1_to_hex(have_obj.objects[i].item->sha1));
+ }
+
+ fprintf(pipe_fd, "\n");
+ fflush(pipe_fd);
+ fclose(pipe_fd);
+ }
+
+
/* We read from pack_objects.err to capture stderr output for
* progress bar, and pack_objects.out to capture the pack data.
*/
@@ -276,7 +301,7 @@ static void create_pack_file(void)
error("git upload-pack: git-pack-objects died with error.");
goto fail;
}
- if (finish_async(&rev_list))
+ if (shallow_nr && finish_async(&rev_list))
goto fail; /* error was already reported */
/* flush the data */
@@ -451,6 +476,7 @@ static void receive_needs(void)
static char line[1000];
int len, depth = 0;
+ shallow_nr = 0;
if (debug_fd)
write_in_full(debug_fd, "#S\n", 3);
for (;;) {
@@ -538,6 +564,7 @@ static void receive_needs(void)
packet_write(1, "shallow %s",
sha1_to_hex(object->sha1));
register_shallow(object->sha1);
+ shallow_nr++;
}
result = result->next;
}
@@ -571,6 +598,8 @@ static void receive_needs(void)
for (i = 0; i < shallows.nr; i++)
register_shallow(shallows.objects[i].item->sha1);
}
+
+ shallow_nr += shallows.nr;
free(shallows.objects);
}
@@ -622,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];