summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/Makefile2
-rw-r--r--Documentation/RelNotes-1.6.3.4.txt36
-rw-r--r--Documentation/RelNotes-1.6.4.txt33
-rw-r--r--Documentation/RelNotes-1.6.5.txt51
-rw-r--r--Documentation/config.txt21
-rw-r--r--Documentation/diff-format.txt8
-rw-r--r--Documentation/fetch-options.txt2
-rw-r--r--Documentation/git-add.txt2
-rw-r--r--Documentation/git-diff-files.txt3
-rw-r--r--Documentation/git-diff-index.txt2
-rw-r--r--Documentation/git-diff-tree.txt3
-rw-r--r--Documentation/git-diff.txt3
-rw-r--r--Documentation/git-fast-export.txt25
-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-ls-files.txt2
-rw-r--r--Documentation/git-merge-base.txt9
-rw-r--r--Documentation/git-merge.txt2
-rw-r--r--Documentation/git-pack-objects.txt7
-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-remote.txt4
-rw-r--r--Documentation/git-rerere.txt2
-rw-r--r--Documentation/git-rev-list.txt24
-rw-r--r--Documentation/git-send-email.txt5
-rw-r--r--Documentation/git-show-branch.txt13
-rw-r--r--Documentation/git-stash.txt17
-rw-r--r--Documentation/git-submodule.txt4
-rw-r--r--Documentation/git-svn.txt15
-rw-r--r--Documentation/git-symbolic-ref.txt4
-rw-r--r--Documentation/git-tag.txt5
-rw-r--r--Documentation/git-verify-pack.txt3
-rw-r--r--Documentation/git.txt10
-rw-r--r--Documentation/gitattributes.txt2
-rw-r--r--Documentation/gitcore-tutorial.txt4
-rw-r--r--Documentation/pt_BR/gittutorial.txt675
-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
-rwxr-xr-xGIT-VERSION-GEN2
l---------RelNotes2
-rw-r--r--builtin-apply.c82
-rw-r--r--builtin-describe.c5
-rw-r--r--builtin-diff.c2
-rw-r--r--builtin-fast-export.c91
-rw-r--r--builtin-grep.c56
-rw-r--r--builtin-init-db.c86
-rw-r--r--builtin-log.c14
-rw-r--r--builtin-merge-base.c2
-rw-r--r--builtin-merge.c2
-rw-r--r--builtin-pack-objects.c6
-rw-r--r--builtin-prune-packed.c29
-rw-r--r--builtin-push.c10
-rw-r--r--builtin-read-tree.c174
-rw-r--r--builtin-receive-pack.c38
-rw-r--r--builtin-reflog.c2
-rw-r--r--builtin-send-pack.c3
-rw-r--r--builtin-show-branch.c6
-rw-r--r--builtin-verify-pack.c67
-rw-r--r--builtin-verify-tag.c21
-rw-r--r--builtin-write-tree.c40
-rw-r--r--cache.h5
-rw-r--r--commit.c6
-rw-r--r--compat/mingw.h5
-rw-r--r--config.c51
-rwxr-xr-xcontrib/completion/git-completion.bash1
-rw-r--r--contrib/emacs/git.el13
-rwxr-xr-xcontrib/hg-to-git/hg-to-git.py2
-rw-r--r--convert.c2
-rw-r--r--entry.c15
-rw-r--r--environment.c1
-rwxr-xr-xgit-am.sh20
-rwxr-xr-xgit-instaweb.sh6
-rwxr-xr-xgit-pull.sh32
-rwxr-xr-xgit-rebase--interactive.sh2
-rwxr-xr-xgit-rebase.sh6
-rwxr-xr-xgit-repack.sh2
-rwxr-xr-xgit-request-pull.sh26
-rwxr-xr-xgit-send-email.perl19
-rwxr-xr-xgit-svn.perl166
-rw-r--r--git.c10
-rw-r--r--gitweb/README6
-rwxr-xr-xgitweb/gitweb.perl16
-rw-r--r--grep.h1
-rw-r--r--help.c4
-rw-r--r--http.c22
-rw-r--r--ll-merge.c4
-rw-r--r--merge-recursive.c28
-rw-r--r--parse-options.c6
-rw-r--r--parse-options.h4
-rw-r--r--run-command.c105
-rw-r--r--run-command.h13
-rw-r--r--send-pack.h1
-rw-r--r--symlinks.c4
-rw-r--r--t/Makefile2
-rw-r--r--t/lib-cvs.sh75
-rw-r--r--t/lib-git-svn.sh2
-rwxr-xr-xt/t0001-init.sh83
-rwxr-xr-xt/t1300-repo-config.sh27
-rwxr-xr-xt/t3404-rebase-interactive.sh2
-rwxr-xr-xt/t3411-rebase-preserve-around-merges.sh2
-rwxr-xr-xt/t3414-rebase-preserve-onto.sh80
-rwxr-xr-xt/t4014-format-patch.sh3
-rwxr-xr-xt/t4020-diff-external.sh2
-rwxr-xr-xt/t4124-apply-ws-rule.sh10
-rwxr-xr-xt/t4132-apply-removal.sh95
-rwxr-xr-xt/t4150-am.sh15
-rwxr-xr-xt/t5510-fetch.sh11
-rwxr-xr-xt/t5520-pull.sh24
-rwxr-xr-xt/t5530-upload-pack-error.sh17
-rwxr-xr-xt/t6010-merge-base.sh18
-rwxr-xr-xt/t6020-merge-df.sh23
-rwxr-xr-xt/t6035-merge-dir-to-symlink.sh93
-rwxr-xr-xt/t6036-recursive-corner-cases.sh55
-rwxr-xr-xt/t7002-grep.sh51
-rwxr-xr-xt/t7700-repack.sh12
-rwxr-xr-xt/t8005-blame-i18n.sh26
-rw-r--r--t/t8005/euc-japan.txt2
-rw-r--r--t/t8005/sjis.txt4
-rw-r--r--t/t8005/utf8.txt4
-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/t9135-git-svn-moved-branch-empty-file.sh7
-rwxr-xr-xt/t9142-git-svn-shallow-clone.sh2
-rwxr-xr-xt/t9143-git-svn-gc.sh53
-rwxr-xr-xt/t9144-git-svn-old-rev_map.sh31
-rwxr-xr-xt/t9145-git-svn-master-branch.sh25
-rwxr-xr-xt/t9301-fast-export.sh96
-rwxr-xr-xt/t9600-cvsimport.sh44
-rwxr-xr-xt/t9601-cvsimport-vendor-branch.sh86
-rw-r--r--t/t9601/cvsroot/.gitattributes1
-rw-r--r--t/t9601/cvsroot/CVSROOT/.gitignore2
-rw-r--r--t/t9601/cvsroot/module/added-imported.txt,v44
-rw-r--r--t/t9601/cvsroot/module/imported-anonymously.txt,v42
-rw-r--r--t/t9601/cvsroot/module/imported-modified-imported.txt,v76
-rw-r--r--t/t9601/cvsroot/module/imported-modified.txt,v59
-rw-r--r--t/t9601/cvsroot/module/imported-once.txt,v43
-rw-r--r--t/t9601/cvsroot/module/imported-twice.txt,v60
-rwxr-xr-xt/t9602-cvsimport-branches-tags.sh79
-rw-r--r--t/t9602/README62
-rw-r--r--t/t9602/cvsroot/.gitattributes1
-rw-r--r--t/t9602/cvsroot/CVSROOT/.gitignore2
-rw-r--r--t/t9602/cvsroot/module/default,v102
-rw-r--r--t/t9602/cvsroot/module/sub1/default,v102
-rw-r--r--t/t9602/cvsroot/module/sub1/subsubA/default,v101
-rw-r--r--t/t9602/cvsroot/module/sub1/subsubB/default,v107
-rw-r--r--t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v59
-rw-r--r--t/t9602/cvsroot/module/sub2/default,v102
-rw-r--r--t/t9602/cvsroot/module/sub2/subsubA/default,v102
-rw-r--r--t/t9602/cvsroot/module/sub3/default,v102
-rwxr-xr-xt/t9603-cvsimport-patchsets.sh40
-rw-r--r--t/t9603/cvsroot/.gitattributes1
-rw-r--r--t/t9603/cvsroot/CVSROOT/.gitignore2
-rw-r--r--t/t9603/cvsroot/module/a,v74
-rw-r--r--t/t9603/cvsroot/module/b,v90
-rw-r--r--t/test-lib.sh10
-rw-r--r--transport.c46
-rw-r--r--transport.h4
-rw-r--r--unpack-trees.h24
-rw-r--r--upload-pack.c49
-rw-r--r--ws.c4
164 files changed, 4697 insertions, 649 deletions
diff --git a/Documentation/Makefile b/Documentation/Makefile
index 7a8037f586..06b0c57b95 100644
--- a/Documentation/Makefile
+++ b/Documentation/Makefile
@@ -84,7 +84,7 @@ endif
#
ifdef ASCIIDOC8
-ASCIIDOC_EXTRA += -a asciidoc7compatible
+ASCIIDOC_EXTRA += -a asciidoc7compatible -a no-inline-literal
endif
ifdef DOCBOOK_XSL_172
ASCIIDOC_EXTRA += -a git-asciidoc-no-roff
diff --git a/Documentation/RelNotes-1.6.3.4.txt b/Documentation/RelNotes-1.6.3.4.txt
new file mode 100644
index 0000000000..cad461bc76
--- /dev/null
+++ b/Documentation/RelNotes-1.6.3.4.txt
@@ -0,0 +1,36 @@
+GIT v1.6.3.4 Release Notes
+==========================
+
+Fixes since v1.6.3.3
+--------------------
+
+ * "git add --no-ignore-errors" did not override configured
+ add.ignore-errors configuration.
+
+ * "git apply --whitespace=fix" did not fix trailing whitespace on an
+ incomplete line.
+
+ * "git branch" opened too many commit objects unnecessarily.
+
+ * "git checkout -f $commit" with a path that is a file (or a symlink) in
+ the work tree to a commit that has a directory at the path issued an
+ unnecessary error message.
+
+ * "git diff -c/--cc" was very inefficient in coalescing the removed lines
+ shared between parents.
+
+ * "git diff -c/--cc" showed removed lines at the beginning of a file
+ incorrectly.
+
+ * "git remote show nickname" did not honor configured
+ remote.nickname.uploadpack when inspecting the branches at the remote.
+
+ * "git request-pull" when talking to the terminal for a preview
+ showed some of the output in the pager.
+
+ * "git request-pull start nickname [end]" did not honor configured
+ remote.nickname.uploadpack when it ran git-ls-remote against the remote
+ repository to learn the current tip of branches.
+
+Includes other documentation updates and minor fixes.
+
diff --git a/Documentation/RelNotes-1.6.4.txt b/Documentation/RelNotes-1.6.4.txt
index f578b8186f..7a904419f7 100644
--- a/Documentation/RelNotes-1.6.4.txt
+++ b/Documentation/RelNotes-1.6.4.txt
@@ -32,13 +32,14 @@ Updates since v1.6.3
* git-svn updates, including a new --authors-prog option to map author
names by invoking an external program, 'git svn reset' to unwind
- 'git svn fetch', support for more than one branches, etc.
+ 'git svn fetch', support for more than one branches, documenting
+ of the useful --minimize-url feature, new "git svn gc" command, etc.
(portability)
* We feed iconv with "UTF-8" instead of "utf8"; the former is
understood more widely. Similarly updated test scripts to use
- encoding names more widely understood (e.g. use "ISO8850-1" instead
+ encoding names more widely understood (e.g. use "ISO8859-1" instead
of "ISO-8859-1").
* Various portability fixes/workarounds for different vintages of
@@ -66,6 +67,12 @@ Updates since v1.6.3
* "git cvsexportcommit" learned -k option to stop CVS keywords expansion
+ * "git fast-export" learned to handle history simplification more
+ gracefully.
+
+ * "git fast-export" learned an option --tag-of-filtered-object to handle
+ dangling tags resulting from history simplification more usefully.
+
* "git grep" learned -p option to show the location of the match using the
same context hunk marker "git diff" uses.
@@ -75,7 +82,7 @@ Updates since v1.6.3
* "git imap-send" is IPv6 aware.
- * "git log --graph" draws graphs more compactly by using horizonal lines
+ * "git log --graph" draws graphs more compactly by using horizontal lines
when able.
* "git log --decorate" shows shorter refnames by stripping well-known
@@ -105,6 +112,8 @@ Updates since v1.6.3
* A major part of the "git bisect" wrapper has moved to C.
+ * Formatting with the new version of AsciiDoc 8.4.1 is now supported.
+
Fixes since v1.6.3
------------------
@@ -123,8 +132,16 @@ v1.6.3.X series.
that chdir around. It now internally records the repository location
as an absolute path when autodetected.
----
-exec >/var/tmp/1
-echo O=$(git describe master)
-O=v1.6.4-rc1-7-gbba0fd2
-git shortlog --no-merges $O..master ^maint
+ * Removing a section with "git config --remove-section", when its
+ section header has a variable definition on the same line, lost
+ that variable definition.
+
+ * "git rebase -p --onto" used to always leave side branches of a merge
+ intact, even when both branches are subject to rewriting.
+
+ * "git repack" used to faithfully follow grafts and considered true
+ parents recorded in the commit object unreachable from the commit.
+ After such a repacking, you cannot remove grafts without corrupting
+ the repository.
+
+ * "git send-email" did not detect erroneous loops in alias expansion.
diff --git a/Documentation/RelNotes-1.6.5.txt b/Documentation/RelNotes-1.6.5.txt
new file mode 100644
index 0000000000..856047d16f
--- /dev/null
+++ b/Documentation/RelNotes-1.6.5.txt
@@ -0,0 +1,51 @@
+GIT v1.6.5 Release Notes
+========================
+
+In git 1.7.0, which is planned to be the release after 1.6.5, "git push"
+into a branch that is currently checked out will be refused by default.
+
+You can choose what should happen upon such a push by setting the
+configuration variable receive.denyCurrentBranch in the receiving
+repository.
+
+Also, "git push $there :$killed" to delete the branch $killed in a remote
+repository $there, when $killed branch is the current branch pointed at by
+its HEAD, will be refused by default.
+
+You can choose what should happen upon such a push by setting the
+configuration variable receive.denyDeleteCurrent in the receiving
+repository.
+
+To ease the transition plan, the receiving repository of such a
+push running this release will issue a big warning when the
+configuration variable is missing. Please refer to:
+
+ http://git.or.cz/gitwiki/GitFaq#non-bare
+ http://thread.gmane.org/gmane.comp.version-control.git/107758/focus=108007
+
+for more details on the reason why this change is needed and the
+transition plan.
+
+Updates since v1.6.4
+--------------------
+
+(subsystems)
+
+(portability)
+
+(performance)
+
+(usability, bells and whistles)
+
+(developers)
+
+Fixes since v1.6.4
+------------------
+
+# All of the fixes in v1.6.4.X maintenance series are included in this
+# release, unless otherwise noted.
+
+# Here are fixes that this release has, but have not been backported to
+# v1.6.4.X series.
+
+
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 6857d2f4a9..2632c5149e 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -49,7 +49,8 @@ There is also a case insensitive alternative `[section.subsection]` syntax.
In this syntax, subsection names follow the same restrictions as for section
names.
-All the other lines are recognized as setting variables, in the form
+All the other lines (and the remainder of the line after the section
+header) are recognized as setting variables, in the form
'name = value'. If there is no equal sign on the line, the entire line
is taken as 'name' and the variable is recognized as boolean "true".
The variable names are case-insensitive and only alphanumeric
@@ -604,7 +605,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::
@@ -1112,7 +1113,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
@@ -1217,12 +1218,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/diff-format.txt b/Documentation/diff-format.txt
index 1eeb1c7683..b71712473e 100644
--- a/Documentation/diff-format.txt
+++ b/Documentation/diff-format.txt
@@ -1,4 +1,7 @@
-The output format from "git-diff-index", "git-diff-tree",
+Raw output format
+-----------------
+
+The raw output format from "git-diff-index", "git-diff-tree",
"git-diff-files" and "git diff --raw" are very similar.
These commands all compare two sets of things; what is
@@ -16,6 +19,9 @@ git-diff-tree [-r] <tree-ish-1> <tree-ish-2> [<pattern>...]::
git-diff-files [<pattern>...]::
compares the index and the files on the filesystem.
+The "git-diff-tree" command begins its ouput by printing the hash of
+what is being compared. After that, all the commands print one output
+line per changed file.
An output line is formatted this way:
diff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt
index d313795fdb..ea3b1bc19f 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 ab1943c712..e67b7e875e 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-diff-files.txt b/Documentation/git-diff-files.txt
index c526141564..4ef03578eb 100644
--- a/Documentation/git-diff-files.txt
+++ b/Documentation/git-diff-files.txt
@@ -43,8 +43,7 @@ omit diff output for unmerged entries and just show "Unmerged".
-q::
Remain silent even on nonexistent files
-Output format
--------------
+
include::diff-format.txt[]
diff --git a/Documentation/git-diff-index.txt b/Documentation/git-diff-index.txt
index 26920d4f63..8b9ed29299 100644
--- a/Documentation/git-diff-index.txt
+++ b/Documentation/git-diff-index.txt
@@ -34,8 +34,6 @@ include::diff-options.txt[]
'git-diff-index' say that all non-checked-out files are up
to date.
-Output format
--------------
include::diff-format.txt[]
Operating Modes
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
index 23b7abd3c6..f2cef1260b 100644
--- a/Documentation/git-diff-tree.txt
+++ b/Documentation/git-diff-tree.txt
@@ -159,8 +159,7 @@ HEAD commits it finds, which is even more interesting.
in case you care).
-Output format
--------------
+
include::diff-format.txt[]
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index a2f192fb75..0ac711230e 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -84,8 +84,7 @@ include::diff-options.txt[]
the diff to the named paths (you can give directory
names and get diff for all files under them).
-Output format
--------------
+
include::diff-format.txt[]
EXAMPLES
diff --git a/Documentation/git-fast-export.txt b/Documentation/git-fast-export.txt
index 0c9eb567cb..75b06f33e7 100644
--- a/Documentation/git-fast-export.txt
+++ b/Documentation/git-fast-export.txt
@@ -36,6 +36,17 @@ when encountering a signed tag. With 'strip', the tags will be made
unsigned, with 'verbatim', they will be silently exported
and with 'warn', they will be exported, but you will see a warning.
+--tag-of-filtered-object=(abort|drop|rewrite)::
+ Specify how to handle tags whose tagged objectis filtered out.
+ Since revisions and files to export can be limited by path,
+ tagged objects may be filtered completely.
++
+When asking to 'abort' (which is the default), this program will die
+when encountering such a tag. With 'drop' it will omit such tags from
+the output. With 'rewrite', if the tagged object is a commit, it will
+rewrite the tag to tag an ancestor commit (via parent rewriting; see
+linkgit:git-rev-list[1])
+
-M::
-C::
Perform move and/or copy detection, as described in the
@@ -71,6 +82,20 @@ marks the same across runs.
allow that. So fake a tagger to be able to fast-import the
output.
+--no-data::
+ Skip output of blob objects and instead refer to blobs via
+ their original SHA-1 hash. This is useful when rewriting the
+ directory structure or history of a repository without
+ touching the contents of individual files. Note that the
+ resulting stream can only be used by a repository which
+ already contains the necessary objects.
+
+[git-rev-list-args...]::
+ A list of arguments, acceptable to 'git-rev-parse' and
+ 'git-rev-list', that specifies the specific objects and references
+ to export. For example, `master\~10..master` causes the
+ current master reference to be exported along with all objects
+ added since its 10th ancestor commit.
EXAMPLES
--------
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index b753c9d76f..8c700200f5 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 1fd0ff2610..eba3cb4998 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 7151d12f34..f081b24d9d 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-ls-files.txt b/Documentation/git-ls-files.txt
index 057a021eb5..021066e95d 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 767486c770..ce5b369985 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 c04ae739ed..af68d694a0 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-pack-objects.txt b/Documentation/git-pack-objects.txt
index 7d4c1a7556..2e4992970e 100644
--- a/Documentation/git-pack-objects.txt
+++ b/Documentation/git-pack-objects.txt
@@ -11,7 +11,8 @@ SYNOPSIS
[verse]
'git pack-objects' [-q] [--no-reuse-delta] [--delta-base-offset] [--non-empty]
[--local] [--incremental] [--window=N] [--depth=N] [--all-progress]
- [--revs [--unpacked | --all]*] [--stdout | base-name] < object-list
+ [--revs [--unpacked | --all]*] [--stdout | base-name]
+ [--keep-true-parents] < object-list
DESCRIPTION
@@ -197,6 +198,10 @@ base-name::
to force the version for the generated pack index, and to force
64-bit index entries on objects located above the given offset.
+--keep-true-parents::
+ With this option, parents that are hidden by grafts are packed
+ nevertheless.
+
Author
------
diff --git a/Documentation/git-prune-packed.txt b/Documentation/git-prune-packed.txt
index b5f26cee13..abfc6b6ead 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 2653388fd8..58d2bd5d4a 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 7160fa1536..4a932b08c6 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-remote.txt b/Documentation/git-remote.txt
index 9e2b4eaa38..82a3d29673 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-rerere.txt b/Documentation/git-rerere.txt
index a53c3cd35b..7dd515b8cc 100644
--- a/Documentation/git-rerere.txt
+++ b/Documentation/git-rerere.txt
@@ -23,7 +23,7 @@ on the initial manual merge, and applying previously recorded
hand resolutions to their corresponding automerge results.
[NOTE]
-You need to set the configuration variable rerere.enabled to
+You need to set the configuration variable rerere.enabled in order to
enable this command.
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index a765cfa4d2..3341d1b62f 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-send-email.txt b/Documentation/git-send-email.txt
index d6b192b7b9..767cf4d4bd 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -142,8 +142,9 @@ user is prompted for a password while the input is masked for privacy.
--smtp-server-port=<port>::
Specifies a port different from the default port (SMTP
- servers typically listen to smtp port 25 and ssmtp port
- 465); symbolic port names (e.g. "submission" instead of 465)
+ servers typically listen to smtp port 25, but may also listen to
+ submission port 587, or the common SSL smtp port 465);
+ symbolic port names (e.g. "submission" instead of 587)
are also accepted. The port can also be set with the
'sendemail.smtpserverport' configuration variable.
diff --git a/Documentation/git-show-branch.txt b/Documentation/git-show-branch.txt
index 89ec5364ec..734336119c 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 1c64a02fe5..2f5ca7b1a3 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 683ba1a1eb..7dd73ae14e 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -14,8 +14,8 @@ SYNOPSIS
'git submodule' [--quiet] status [--cached] [--] [<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>...]
+ [--reference <repository>] [--merge] [--] [<path>...]
+'git submodule' [--quiet] summary [--cached] [--summary-limit <n>] [commit] [--] [<path>...]
'git submodule' [--quiet] foreach <command>
'git submodule' [--quiet] sync [--] [<path>...]
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index 10af599b44..22a0389f1e 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -80,6 +80,17 @@ COMMANDS
When passed to 'init' or 'clone' this regular expression will
be preserved as a config key. See 'fetch' for a description
of '--ignore-paths'.
+--no-minimize-url;;
+ When tracking multiple directories (using --stdlayout,
+ --branches, or --tags options), git svn will attempt to connect
+ to the root (or highest allowed level) of the Subversion
+ repository. This default allows better tracking of history if
+ entire projects are moved within a repository, but may cause
+ issues on repositories where read access restrictions are in
+ place. Passing '--no-minimize-url' will allow git svn to
+ accept URLs as-is without attempting to connect to a higher
+ level directory. This option is off by default when only
+ one URL/branch is tracked (it would do little good).
'fetch'::
Fetch unfetched revisions from the Subversion remote we are
@@ -338,6 +349,10 @@ Any other arguments are passed directly to 'git log'
Shows the Subversion externals. Use -r/--revision to specify a
specific revision.
+'gc'::
+ Compress $GIT_DIR/svn/<refname>/unhandled.log files in .git/svn
+ and remove $GIT_DIR/svn/<refname>index files in .git/svn.
+
'reset'::
Undoes the effects of 'fetch' back to the specified revision.
This allows you to re-'fetch' an SVN revision. Normally the
diff --git a/Documentation/git-symbolic-ref.txt b/Documentation/git-symbolic-ref.txt
index 210fde03a1..6392538807 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 fa733214ab..1118ce22dc 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -17,7 +17,10 @@ SYNOPSIS
DESCRIPTION
-----------
-Adds a 'tag' reference in `.git/refs/tags/`
+
+Adds a 'tag' reference in `.git/refs/tags/`. The tag <name> must pass
+linkgit:git-check-ref-format[1] which basicly means that control characters,
+space, ~, ^, :, ?, *, [ and \ are prohibited.
Unless `-f` is given, the tag must not yet exist in
`.git/refs/tags/` directory.
diff --git a/Documentation/git-verify-pack.txt b/Documentation/git-verify-pack.txt
index c8611632d1..d791a80819 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,6 +23,7 @@ OPTIONS
The idx files to verify.
-v::
+--verbose::
After verifying the pack, show list of objects contained
in the pack.
\--::
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 6fa0310e05..5832c752e1 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,9 +43,15 @@ 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.3.3/git.html[documentation for release 1.6.3.3]
+* link:v1.6.4/git.html[documentation for release 1.6.4]
* release notes for
+ link:RelNotes-1.6.4.txt[1.6.4].
+
+* link:v1.6.3.4/git.html[documentation for release 1.6.3.4]
+
+* release notes for
+ link:RelNotes-1.6.3.4.txt[1.6.3.4],
link:RelNotes-1.6.3.3.txt[1.6.3.3],
link:RelNotes-1.6.3.2.txt[1.6.3.2],
link:RelNotes-1.6.3.1.txt[1.6.3.1],
@@ -321,7 +327,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 aaa073efc8..1195e83b6e 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 7ba5e589d7..b3640c4e64 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/pt_BR/gittutorial.txt b/Documentation/pt_BR/gittutorial.txt
new file mode 100644
index 0000000000..81e7ad7df4
--- /dev/null
+++ b/Documentation/pt_BR/gittutorial.txt
@@ -0,0 +1,675 @@
+gittutorial(7)
+==============
+
+NAME
+----
+gittutorial - Um tutorial de introdução ao git (para versão 1.5.1 ou mais nova)
+
+SYNOPSIS
+--------
+git *
+
+DESCRIPTION
+-----------
+
+Este tutorial explica como importar um novo projeto para o git,
+adicionar mudanças a ele, e compartilhar mudanças com outros
+desenvolvedores.
+
+Se, ao invés disso, você está interessado primariamente em usar git para
+obter um projeto, por exemplo, para testar a última versão, você pode
+preferir começar com os primeiros dois capítulos de
+link:user-manual.html[O Manual do Usuário Git].
+
+Primeiro, note que você pode obter documentação para um comando como
+`git log --graph` com:
+
+------------------------------------------------
+$ man git-log
+------------------------------------------------
+
+ou:
+
+------------------------------------------------
+$ git help log
+------------------------------------------------
+
+Com a última forma, você pode usar o visualizador de manual de sua
+escolha; veja linkgit:git-help[1] para maior informação.
+
+É uma boa idéia informar ao git seu nome e endereço público de email
+antes de fazer qualquer operação. A maneira mais fácil de fazê-lo é:
+
+------------------------------------------------
+$ git config --global user.name "Seu Nome Vem Aqui"
+$ git config --global user.email voce@seudominio.exemplo.com
+------------------------------------------------
+
+
+Importando um novo projeto
+-----------------------
+
+Assuma que você tem um tarball project.tar.gz com seu trabalho inicial.
+Você pode colocá-lo sob controle de revisão git da seguinte forma:
+
+------------------------------------------------
+$ tar xzf project.tar.gz
+$ cd project
+$ git init
+------------------------------------------------
+
+Git irá responder
+
+------------------------------------------------
+Initialized empty Git repository in .git/
+------------------------------------------------
+
+Você agora iniciou seu diretório de trabalho--você deve ter notado um
+novo diretório criado, com o nome de ".git".
+
+A seguir, diga ao git para gravar um instantâneo do conteúdo de todos os
+arquivos sob o diretório corrente (note o '.'), com 'git-add':
+
+------------------------------------------------
+$ git add .
+------------------------------------------------
+
+Este instantâneo está agora armazenado em uma área temporária que o git
+chama de "index" ou índice. Você pode armazenar permanentemente o
+conteúdo do índice no repositório com 'git-commit':
+
+------------------------------------------------
+$ git commit
+------------------------------------------------
+
+Isto vai te pedir por uma mensagem de commit. Você agora gravou sua
+primeira versão de seu projeto no git.
+
+Fazendo mudanças
+--------------
+
+Modifique alguns arquivos, e, então, adicione seu conteúdo atualizado ao
+índice:
+
+------------------------------------------------
+$ git add file1 file2 file3
+------------------------------------------------
+
+Você está agora pronto para fazer o commit. Você pode ver o que está
+para ser gravado usando 'git-diff' com a opção --cached:
+
+------------------------------------------------
+$ git diff --cached
+------------------------------------------------
+
+(Sem --cached, o comando 'git-diff' irá te mostrar quaisquer mudanças
+que você tenha feito mas ainda não adicionou ao índice.) Você também
+pode obter um breve sumário da situação com 'git-status':
+
+------------------------------------------------
+$ git status
+# On branch master
+# Changes to be committed:
+# (use "git reset HEAD <file>..." to unstage)
+#
+# modified: file1
+# modified: file2
+# modified: file3
+#
+------------------------------------------------
+
+Se você precisar fazer qualquer outro ajuste, faça-o agora, e, então,
+adicione qualquer conteúdo modificado ao índice. Finalmente, grave suas
+mudanças com:
+
+------------------------------------------------
+$ git commit
+------------------------------------------------
+
+Isto irá novamente te pedir por uma mensagem descrevendo a mudança, e,
+então, gravar a nova versão do projeto.
+
+Alternativamente, ao invés de executar 'git-add' antes, você pode usar
+
+------------------------------------------------
+$ git commit -a
+------------------------------------------------
+
+o que irá automaticamente notar quaisquer arquivos modificados (mas não
+novos), adicioná-los ao índices, e gravar, tudo em um único passo.
+
+Uma nota em mensagens de commit: Apesar de não ser exigido, é uma boa
+idéia começar a mensagem com uma simples e curta (menos de 50
+caracteres) linha sumarizando a mudança, seguida de uma linha em branco
+e, então, uma descrição mais detalhada. Ferramentas que transformam
+commits em email, por exemplo, usam a primeira linha no campo de
+cabeçalho Subject: e o resto no corpo.
+
+Git rastreia conteúdo, não arquivos
+----------------------------
+
+Muitos sistemas de controle de revisão provêem um comando `add` que diz
+ao sistema para começar a rastrear mudanças em um novo arquivo. O
+comando `add` do git faz algo mais simples e mais poderoso: 'git-add' é
+usado tanto para arquivos novos e arquivos recentemente modificados, e
+em ambos os casos, ele tira o instantâneo dos arquivos dados e armazena
+o conteúdo no índice, pronto para inclusão do próximo commit.
+
+Visualizando história do projeto
+-----------------------
+
+Em qualquer ponto você pode visualizar a história das suas mudanças
+usando
+
+------------------------------------------------
+$ git log
+------------------------------------------------
+
+Se você também quer ver a diferença completa a cada passo, use
+
+------------------------------------------------
+$ git log -p
+------------------------------------------------
+
+Geralmente, uma visão geral da mudança é útil para ter a sensação de
+cada passo
+
+------------------------------------------------
+$ git log --stat --summary
+------------------------------------------------
+
+Gerenciando "branches"/ramos
+-----------------
+
+Um simples repositório git pode manter múltiplos ramos de
+desenvolvimento. Para criar um novo ramo chamado "experimental", use
+
+------------------------------------------------
+$ git branch experimental
+------------------------------------------------
+
+Se você executar agora
+
+------------------------------------------------
+$ git branch
+------------------------------------------------
+
+você vai obter uma lista de todos os ramos existentes:
+
+------------------------------------------------
+ experimental
+* master
+------------------------------------------------
+
+O ramo "experimental" é o que você acaba de criar, e o ramo "master" é o
+ramo padrão que foi criado pra você automaticamente. O asterisco marca
+o ramo em que você está atualmente; digite
+
+------------------------------------------------
+$ git checkout experimental
+------------------------------------------------
+
+para mudar para o ramo experimental. Agora edite um arquivo, grave a
+mudança, e mude de volta para o ramo master:
+
+------------------------------------------------
+(edita arquivo)
+$ git commit -a
+$ git checkout master
+------------------------------------------------
+
+Verifique que a mudança que você fez não está mais visível, já que ela
+foi feita no ramo experimental e você está de volta ao ramo master.
+
+Você pode fazer uma mudança diferente no ramo master:
+
+------------------------------------------------
+(edit file)
+$ git commit -a
+------------------------------------------------
+
+neste ponto, os dois ramos divergiram, com diferentes mudanças feitas em
+cada um. Para unificar as mudanças feitas no experimental para o
+master, execute
+
+------------------------------------------------
+$ git merge experimental
+------------------------------------------------
+
+Se as mudanças não conflitarem, estará pronto. Se existirem conflitos,
+marcadores serão deixados nos arquivos problemáticos exibindo o
+conflito;
+
+------------------------------------------------
+$ git diff
+------------------------------------------------
+
+vai exibir isto. Após você editar os arquivos para resolver os
+conflitos,
+
+------------------------------------------------
+$ git commit -a
+------------------------------------------------
+
+irá gravar o resultado da unificação. Finalmente,
+
+------------------------------------------------
+$ gitk
+------------------------------------------------
+
+vai mostrar uma bela representação gráfica da história resultante.
+
+Neste ponto você pode remover seu ramo experimental com
+
+------------------------------------------------
+$ git branch -d experimental
+------------------------------------------------
+
+Este comando garante que as mudanças no ramo experimental já estão no
+ramo atual.
+
+Se você desenvolve em um ramo ideia-louca, e se arrepende, você pode
+sempre remover o ramo com
+
+-------------------------------------
+$ git branch -D ideia-louca
+-------------------------------------
+
+Ramos são baratos e fáceis, então isto é uma boa maneira de experimentar
+alguma coisa.
+
+Usando git para colaboração
+---------------------------
+
+Suponha que Alice começou um novo projeto com um repositório git em
+/home/alice/project, e que Bob, que tem um diretório home na mesma
+máquina, quer contribuir.
+
+Bob começa com:
+
+------------------------------------------------
+bob$ git clone /home/alice/project myrepo
+------------------------------------------------
+
+Isso cria um novo diretório "myrepo" contendo um clone do repositório de
+Alice. O clone está no mesmo pé que o projeto original, possuindo sua
+própria cópia da história do projeto original.
+
+Bob então faz algumas mudanças e as grava:
+
+------------------------------------------------
+(editar arquivos)
+bob$ git commit -a
+(repetir conforme necessário)
+------------------------------------------------
+
+Quanto está pronto, ele diz a Alice para puxar as mudanças do
+repositório em /home/bob/myrepo. Ela o faz com:
+
+------------------------------------------------
+alice$ cd /home/alice/project
+alice$ git pull /home/bob/myrepo master
+------------------------------------------------
+
+Isto unifica as mudanças do ramo "master" do Bob ao ramo atual de Alice.
+Se Alice fez suas próprias mudanças no intervalo, ela, então, pode
+precisar corrigir manualmente quaisquer conflitos. (Note que o argumento
+"master" no comando acima é, de fato, desnecessário, já que é o padrão.)
+
+O comando "pull" executa, então, duas operações: ele obtém mudanças de
+um ramo remoto, e, então, as unifica no ramo atual.
+
+Note que, em geral, Alice gostaria que suas mudanças locais fossem
+gravadas antes de iniciar este "pull". Se o trabalho de Bob conflita
+com o que Alice fez desde que suas histórias se ramificaram, Alice irá
+usar seu diretório de trabalho e o índice para resolver conflitos, e
+mudanças locais existentes irão interferir com o processo de resolução
+de conflitos (git ainda irá realizar a obtenção mas irá se recusar a
+unificar --- Alice terá que se livrar de suas mudanças locais de alguma
+forma e puxar de novo quando isso acontecer).
+
+Alice pode espiar o que Bob fez sem unificar primeiro, usando o comando
+"fetch"; isto permite Alice inspecionar o que Bob fez, usando um símbolo
+especial "FETCH_HEAD", com o fim de determinar se ele tem alguma coisa
+que vale puxar, assim:
+
+------------------------------------------------
+alice$ git fetch /home/bob/myrepo master
+alice$ git log -p HEAD..FETCH_HEAD
+------------------------------------------------
+
+Esta operação é segura mesmo se Alice tem mudanças locais não gravadas.
+A notação de intervalo "HEAD..FETCH_HEAD" significa mostrar tudo que é
+alcançável de FETCH_HEAD mas exclua tudo o que é alcançável de HEAD.
+Alice já sabe tudo que leva a seu estado atual (HEAD), e revisa o que Bob
+tem em seu estado (FETCH_HEAD) que ela ainda não viu com esse comando.
+
+Se Alice quer visualizar o que Bob fez desde que suas histórias se
+ramificaram, ela pode disparar o seguinte comando:
+
+------------------------------------------------
+$ gitk HEAD..FETCH_HEAD
+------------------------------------------------
+
+Isto usa a mesma notação de intervalo que vimos antes com 'git log'.
+
+Alice pode querer ver o que ambos fizeram desde que ramificaram. Ela
+pode usar a forma com três pontos ao invés da forma com dois pontos:
+
+------------------------------------------------
+$ gitk HEAD...FETCH_HEAD
+------------------------------------------------
+
+Isto significa "mostre tudo que é alcançável de qualquer um deles, mas
+exclua tudo que é alcançável a partir de ambos".
+
+Por favor, note que essas notações de intervalo podem ser usadas tanto
+com gitk quanto com "git log".
+
+Após inspecionar o que Bob fez, se não há nada urgente, Alice pode
+decidir continuar trabalhando sem puxar de Bob. Se a história de Bob
+tem alguma coisa que Alice precisa imediatamente, Alice pode optar por
+separar seu trabalho em progresso primeiro, fazer um "pull", e, então,
+finalmente, retomar seu trabalho em progresso em cima da história
+resultante.
+
+Quando você está trabalhando em um pequeno grupo unido, não é incomum
+interagir com o mesmo repositório várias e várias vezes. Definindo um
+repositório remoto antes de tudo, você pode fazê-lo mais facilmente:
+
+------------------------------------------------
+alice$ git remote add bob /home/bob/myrepo
+------------------------------------------------
+
+Com isso, Alice pode executar a primeira parte da operação "pull" usando
+o comando 'git-fetch' sem unificar suas mudanças com seu próprio ramo,
+usando:
+
+-------------------------------------
+alice$ git fetch bob
+-------------------------------------
+
+Diferente da forma longa, quando Alice obteve de Bob usando um
+repositório remoto antes definido com 'git-remote', o que foi obtido é
+armazenado em um ramo remoto, neste caso `bob/master`. Então, após isso:
+
+-------------------------------------
+alice$ git log -p master..bob/master
+-------------------------------------
+
+mostra uma lista de todas as mudanças que Bob fez desde que ramificou do
+ramo master de Alice.
+
+Após examinar essas mudanças, Alice pode unificá-las em seu ramo master:
+
+-------------------------------------
+alice$ git merge bob/master
+-------------------------------------
+
+Esse `merge` pode também ser feito puxando de seu próprio ramo remoto,
+assim:
+
+-------------------------------------
+alice$ git pull . remotes/bob/master
+-------------------------------------
+
+Note que 'git pull' sempre unifica ao ramo atual, independente do que
+mais foi passado na linha de comando.
+
+Depois, Bob pode atualizar seu repositório com as últimas mudanças de
+Alice, usando
+
+-------------------------------------
+bob$ git pull
+-------------------------------------
+
+Note que ele não precisa dar o caminho do repositório de Alice; quando
+Bob clonou seu repositório, o git armazenou a localização de seu
+repositório na configuração do mesmo, e essa localização é usada
+para puxar:
+
+-------------------------------------
+bob$ git config --get remote.origin.url
+/home/alice/project
+-------------------------------------
+
+(A configuração completa criada por 'git-clone' é visível usando `git
+config -l`, e a página de manual linkgit:git-config[1] explica o
+significado de cada opção.)
+
+Git também mantém uma cópia limpa do ramo master de Alice sob o nome
+"origin/master":
+
+-------------------------------------
+bob$ git branch -r
+ origin/master
+-------------------------------------
+
+Se Bob decidir depois em trabalhar em um host diferente, ele ainda pode
+executar clones e puxar usando o protocolo ssh:
+
+-------------------------------------
+bob$ git clone alice.org:/home/alice/project myrepo
+-------------------------------------
+
+Alternativamente, o git tem um protocolo nativo, ou pode usar rsync ou
+http; veja linkgit:git-pull[1] para detalhes.
+
+Git pode também ser usado em um modo parecido com CVS, com um
+repositório central para o qual vários usuários empurram modificações;
+veja linkgit:git-push[1] e linkgit:gitcvs-migration[7].
+
+Explorando história
+-----------------
+
+A história no git é representada como uma série de commits
+interrelacionados. Nós já vimos que o comando 'git-log' pode listar
+esses commits. Note que a primeira linha de cada entrada no log também
+dá o nome para o commit:
+
+-------------------------------------
+$ git log
+commit c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
+Author: Junio C Hamano <junkio@cox.net>
+Date: Tue May 16 17:18:22 2006 -0700
+
+ merge-base: Clarify the comments on post processing.
+-------------------------------------
+
+Nós podemos dar este nome ao 'git-show' para ver os detalhes sobre este
+commit.
+
+-------------------------------------
+$ git show c82a22c39cbc32576f64f5c6b3f24b99ea8149c7
+-------------------------------------
+
+Mas há outras formas de se referir aos commits. Você pode usar qualquer
+parte inicial do nome que seja longo o bastante para identificar
+unicamente o commit:
+
+-------------------------------------
+$ git show c82a22c39c # os primeiros caracteres do nome são o bastante
+ # usualmente
+$ git show HEAD # a ponta do ramo atual
+$ git show experimental # a ponta do ramo "experimental"
+-------------------------------------
+
+Todo commit normalmente tem um commit "pai" que aponta para o estado
+anterior do projeto:
+
+-------------------------------------
+$ git show HEAD^ # para ver o pai de HEAD
+$ git show HEAD^^ # para ver o avô de HEAD
+$ git show HEAD~4 # para ver o trisavô de HEAD
+-------------------------------------
+
+Note que commits de unificação podem ter mais de um pai:
+
+-------------------------------------
+$ git show HEAD^1 # mostra o primeiro pai de HEAD (o mesmo que HEAD^)
+$ git show HEAD^2 # mostra o segundo pai de HEAD
+-------------------------------------
+
+Você também pode dar aos commits nomes à sua escolha; após executar
+
+-------------------------------------
+$ git tag v2.5 1b2e1d63ff
+-------------------------------------
+
+você pode se referir a 1b2e1d63ff pelo nome "v2.5". Se você pretende
+compartilhar esse nome com outras pessoas (por exemplo, para identificar
+uma versão de lançamento), você deveria criar um objeto "tag", e talvez
+assiná-lo; veja linkgit:git-tag[1] para detalhes.
+
+Qualquer comando git que precise conhecer um commit pode receber
+quaisquer desses nomes. Por exemplo:
+
+-------------------------------------
+$ git diff v2.5 HEAD # compara o HEAD atual com v2.5
+$ git branch stable v2.5 # inicia um novo ramo chamado "stable" baseado
+ # em v2.5
+$ git reset --hard HEAD^ # reseta seu ramo atual e seu diretório de
+ # trabalho a seu estado em HEAD^
+-------------------------------------
+
+Seja cuidadoso com o último comando: além de perder quaisquer mudanças
+em seu diretório de trabalho, ele também remove todos os commits
+posteriores desse ramo. Se esse ramo é o único ramo contendo esses
+commits, eles serão perdidos. Também, não use 'git-reset' num ramo
+publicamente visível de onde outros desenvolvedores puxam, já que vai
+forçar unificações desnecessárias para que outros desenvolvedores limpem
+a história. Se você precisa desfazer mudanças que você empurrou, use
+'git-revert' no lugar.
+
+O comando 'git-grep' pode buscar strings em qualquer versão de seu
+projeto, então
+
+-------------------------------------
+$ git grep "hello" v2.5
+-------------------------------------
+
+procura por todas as ocorrências de "hello" em v2.5.
+
+Se você deixar de fora o nome do commit, 'git-grep' irá procurar
+quaisquer dos arquivos que ele gerencia no diretório corrente. Então
+
+-------------------------------------
+$ git grep "hello"
+-------------------------------------
+
+é uma forma rápida de buscar somente os arquivos que são rastreados pelo
+git.
+
+Muitos comandos git também recebem um conjunto de commits, o que pode
+ser especificado de várias formas. Aqui estão alguns exemplos com 'git-log':
+
+-------------------------------------
+$ git log v2.5..v2.6 # commits entre v2.5 e v2.6
+$ git log v2.5.. # commits desde v2.5
+$ git log --since="2 weeks ago" # commits das últimas 2 semanas
+$ git log v2.5.. Makefile # commits desde v2.5 que modificam
+ # Makefile
+-------------------------------------
+
+Você também pode dar ao 'git-log' um "intervalo" de commits onde o
+primeiro não é necessariamente um ancestral do segundo; por exemplo, se
+as pontas dos ramos "stable" e "master" divergiram de um commit
+comum algum tempo atrás, então
+
+-------------------------------------
+$ git log stable..master
+-------------------------------------
+
+irá listar os commits feitos no ramo "master" mas não no ramo
+"stable", enquanto
+
+-------------------------------------
+$ git log master..stable
+-------------------------------------
+
+irá listar a lista de commits feitos no ramo "stable" mas não no ramo
+"master".
+
+O comando 'git-log' tem uma fraqueza: ele precisa mostrar os commits em
+uma lista. Quando a história tem linhas de desenvolvimento que
+divergiram e então foram unificadas novamente, a ordem em que 'git-log'
+apresenta essas mudanças é irrelevante.
+
+A maioria dos projetos com múltiplos contribuidores (como o kernel
+Linux, ou o próprio git) tem unificações frequentes, e 'gitk' faz um
+trabalho melhor de visualizar sua história. Por exemplo,
+
+-------------------------------------
+$ gitk --since="2 weeks ago" drivers/
+-------------------------------------
+
+permite a você navegar em quaisquer commits desde as últimas duas semanas
+de commits que modificaram arquivos sob o diretório "drivers". (Nota:
+você pode ajustar as fontes do gitk segurando a tecla control enquanto
+pressiona "-" ou "+".)
+
+Finalmente, a maioria dos comandos que recebem nomes de arquivo permitirão
+também, opcionalmente, preceder qualquer nome de arquivo por um
+commit, para especificar uma versão particular do arquivo:
+
+-------------------------------------
+$ git diff v2.5:Makefile HEAD:Makefile.in
+-------------------------------------
+
+Você pode usar 'git-show' para ver tal arquivo:
+
+-------------------------------------
+$ git show v2.5:Makefile
+-------------------------------------
+
+Próximos passos
+----------
+
+Este tutorial deve ser o bastante para operar controle de revisão
+distribuído básico para seus projetos. No entanto, para entender
+plenamente a profundidade e o poder do git você precisa entender duas
+idéias simples nas quais ele se baseia:
+
+ * A base de objetos é um sistema bem elegante usado para armazenar a
+ história de seu projeto--arquivos, diretórios, e commits.
+
+ * O arquivo de índice é um cache do estado de uma árvore de diretório,
+ usado para criar commits, restaurar diretórios de trabalho, e
+ armazenar as várias árvores envolvidas em uma unificação.
+
+A parte dois deste tutorial explica a base de objetos, o arquivo de
+índice, e algumas outras coisinhas que você vai precisar pra usar o
+máximo do git. Você pode encontrá-la em linkgit:gittutorial-2[7].
+
+Se você não quiser continuar com o tutorial agora nesse momento, algumas
+outras digressões que podem ser interessantes neste ponto são:
+
+ * linkgit:git-format-patch[1], linkgit:git-am[1]: Estes convertem
+ séries de commits em patches para email, e vice-versa, úteis para
+ projetos como o kernel Linux que dependem fortemente de patches
+ enviados por email.
+
+ * linkgit:git-bisect[1]: Quando há uma regressão em seu projeto, uma
+ forma de rastrear um bug é procurando pela história para encontrar o
+ commit culpado. Git bisect pode ajudar a executar uma busca binária
+ por esse commit. Ele é inteligente o bastante para executar uma
+ busca próxima da ótima mesmo no caso de uma história complexa
+ não-linear com muitos ramos unificados.
+
+ * link:everyday.html[GIT diariamente com 20 e tantos comandos]
+
+ * linkgit:gitcvs-migration[7]: Git para usuários de CVS.
+
+VEJA TAMBÉM
+--------
+linkgit:gittutorial-2[7],
+linkgit:gitcvs-migration[7],
+linkgit:gitcore-tutorial[7],
+linkgit:gitglossary[7],
+linkgit:git-help[1],
+link:everyday.html[git diariamente],
+link:user-manual.html[O Manual do Usuário git]
+
+GIT
+---
+Parte da suite linkgit:git[1].
diff --git a/Documentation/technical/api-run-command.txt b/Documentation/technical/api-run-command.txt
index 2efe7a40be..b26c28133c 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 e3ddf91284..55b728632c 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 0b88a51d0b..67ebffa568 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/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 39cde784c9..d7d9a9a063 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v1.6.3.GIT
+DEF_VER=v1.6.4.GIT
LF='
'
diff --git a/RelNotes b/RelNotes
index f8e49a5070..b62449d2e2 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes-1.6.4.txt \ No newline at end of file
+Documentation/RelNotes-1.6.5.txt \ No newline at end of file
diff --git a/builtin-apply.c b/builtin-apply.c
index dc0ff5e08a..39dc96ae02 100644
--- a/builtin-apply.c
+++ b/builtin-apply.c
@@ -458,6 +458,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 +563,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);
diff --git a/builtin-describe.c b/builtin-describe.c
index 7a662980d1..df67a733ae 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 2e51f408f9..ffcdd055ca 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-fast-export.c b/builtin-fast-export.c
index ca198250c3..b0a4029c94 100644
--- a/builtin-fast-export.c
+++ b/builtin-fast-export.c
@@ -23,8 +23,10 @@ static const char *fast_export_usage[] = {
};
static int progress;
-static enum { VERBATIM, WARN, STRIP, ABORT } signed_tag_mode = ABORT;
+static enum { ABORT, VERBATIM, WARN, STRIP } signed_tag_mode = ABORT;
+static enum { ERROR, DROP, REWRITE } tag_of_filtered_mode = ABORT;
static int fake_missing_tagger;
+static int no_data;
static int parse_opt_signed_tag_mode(const struct option *opt,
const char *arg, int unset)
@@ -42,6 +44,20 @@ static int parse_opt_signed_tag_mode(const struct option *opt,
return 0;
}
+static int parse_opt_tag_of_filtered_mode(const struct option *opt,
+ const char *arg, int unset)
+{
+ if (unset || !strcmp(arg, "abort"))
+ tag_of_filtered_mode = ABORT;
+ else if (!strcmp(arg, "drop"))
+ tag_of_filtered_mode = DROP;
+ else if (!strcmp(arg, "rewrite"))
+ tag_of_filtered_mode = REWRITE;
+ else
+ return error("Unknown tag-of-filtered mode: %s", arg);
+ return 0;
+}
+
static struct decoration idnums;
static uint32_t last_idnum;
@@ -101,6 +117,9 @@ static void handle_object(const unsigned char *sha1)
char *buf;
struct object *object;
+ if (no_data)
+ return;
+
if (is_null_sha1(sha1))
return;
@@ -158,7 +177,7 @@ static void show_filemodify(struct diff_queue_struct *q,
* Links refer to objects in another repositories;
* output the SHA-1 verbatim.
*/
- if (S_ISGITLINK(spec->mode))
+ if (no_data || S_ISGITLINK(spec->mode))
printf("M %06o %s %s\n", spec->mode,
sha1_to_hex(spec->sha1), spec->path);
else {
@@ -289,6 +308,23 @@ static void handle_tag(const char *name, struct tag *tag)
char *buf;
const char *tagger, *tagger_end, *message;
size_t message_size = 0;
+ struct object *tagged;
+ int tagged_mark;
+ struct commit *p;
+
+ /* Trees have no identifer in fast-export output, thus we have no way
+ * to output tags of trees, tags of tags of trees, etc. Simply omit
+ * such tags.
+ */
+ tagged = tag->tagged;
+ while (tagged->type == OBJ_TAG) {
+ tagged = ((struct tag *)tagged)->tagged;
+ }
+ if (tagged->type == OBJ_TREE) {
+ warning("Omitting tag %s,\nsince tags of trees (or tags of tags of trees, etc.) are not supported.",
+ sha1_to_hex(tag->object.sha1));
+ return;
+ }
buf = read_sha1_file(tag->object.sha1, &type, &size);
if (!buf)
@@ -333,10 +369,45 @@ static void handle_tag(const char *name, struct tag *tag)
}
}
+ /* handle tag->tagged having been filtered out due to paths specified */
+ tagged = tag->tagged;
+ tagged_mark = get_object_mark(tagged);
+ if (!tagged_mark) {
+ switch(tag_of_filtered_mode) {
+ case ABORT:
+ die ("Tag %s tags unexported object; use "
+ "--tag-of-filtered-object=<mode> to handle it.",
+ sha1_to_hex(tag->object.sha1));
+ case DROP:
+ /* Ignore this tag altogether */
+ return;
+ case REWRITE:
+ if (tagged->type != OBJ_COMMIT) {
+ die ("Tag %s tags unexported %s!",
+ sha1_to_hex(tag->object.sha1),
+ typename(tagged->type));
+ }
+ p = (struct commit *)tagged;
+ for (;;) {
+ if (p->parents && p->parents->next)
+ break;
+ if (p->object.flags & UNINTERESTING)
+ break;
+ if (!(p->object.flags & TREESAME))
+ break;
+ if (!p->parents)
+ die ("Can't find replacement commit for tag %s\n",
+ sha1_to_hex(tag->object.sha1));
+ p = p->parents->item;
+ }
+ tagged_mark = get_object_mark(&p->object);
+ }
+ }
+
if (!prefixcmp(name, "refs/tags/"))
name += 10;
printf("tag %s\nfrom :%d\n%.*s%sdata %d\n%.*s\n",
- name, get_object_mark(tag->tagged),
+ name, tagged_mark,
(int)(tagger_end - tagger), tagger,
tagger == tagger_end ? "" : "\n",
(int)message_size, (int)message_size, message ? message : "");
@@ -504,12 +575,18 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
OPT_CALLBACK(0, "signed-tags", &signed_tag_mode, "mode",
"select handling of signed tags",
parse_opt_signed_tag_mode),
+ OPT_CALLBACK(0, "tag-of-filtered-object", &tag_of_filtered_mode, "mode",
+ "select handling of tags that tag filtered objects",
+ parse_opt_tag_of_filtered_mode),
OPT_STRING(0, "export-marks", &export_filename, "FILE",
"Dump marks to this file"),
OPT_STRING(0, "import-marks", &import_filename, "FILE",
"Import marks from this file"),
OPT_BOOLEAN(0, "fake-missing-tagger", &fake_missing_tagger,
"Fake a tagger when tags lack one"),
+ { OPTION_NEGBIT, 0, "data", &no_data, NULL,
+ "Skip output of blob data",
+ PARSE_OPT_NOARG | PARSE_OPT_NEGHELP, NULL, 1 },
OPT_END()
};
@@ -520,6 +597,9 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
git_config(git_default_config, NULL);
init_revisions(&revs, prefix);
+ revs.topo_order = 1;
+ revs.show_source = 1;
+ revs.rewrite_parents = 1;
argc = setup_revisions(argc, argv, &revs, NULL);
argc = parse_options(argc, argv, prefix, options, fast_export_usage, 0);
if (argc > 1)
@@ -530,18 +610,13 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
get_tags_and_duplicates(&revs.pending, &extra_refs);
- revs.topo_order = 1;
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
revs.diffopt.format_callback = show_filemodify;
DIFF_OPT_SET(&revs.diffopt, RECURSIVE);
while ((commit = get_revision(&revs))) {
if (has_unshown_parent(commit)) {
- struct commit_list *parent = commit->parents;
add_object_array(&commit->object, NULL, &commits);
- for (; parent; parent = parent->next)
- if (!parent->item->util)
- parent->item->util = commit->util;
}
else {
handle_commit(commit, &revs);
diff --git a/builtin-grep.c b/builtin-grep.c
index f477659100..ad0e0a5385 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 4a5600631c..dd84caecbc 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 0c2fa0ae2d..3817bf1186 100644
--- a/builtin-log.c
+++ b/builtin-log.c
@@ -27,6 +27,10 @@ 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)
{
@@ -61,6 +65,8 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
rev->show_decorations = 1;
} else if (!strcmp(arg, "--source")) {
rev->show_source = 1;
+ } else if (!strcmp(arg, "-h")) {
+ usage(builtin_log_usage);
} else
die("unrecognized argument: %s", arg);
}
@@ -257,7 +263,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 +335,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 +354,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;
diff --git a/builtin-merge-base.c b/builtin-merge-base.c
index a6ec2f7ab7..54e7ec2237 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 82b546689c..0b12fb3155 100644
--- a/builtin-merge.c
+++ b/builtin-merge.c
@@ -594,7 +594,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 a27c2f6277..9cc8a8451d 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;
@@ -2255,6 +2255,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
die("bad %s", arg);
continue;
}
+ if (!strcmp(arg, "--keep-true-parents")) {
+ grafts_replace_parents = 0;
+ continue;
+ }
usage(pack_usage);
}
diff --git a/builtin-prune-packed.c b/builtin-prune-packed.c
index 00590b1c3c..be99eb0ac4 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-push.c b/builtin-push.c
index 1d92e22f0a..67f6d96fbe 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 82e25eaa07..9c2d634d6d 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,19 @@ 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;
- }
-
- if (!strcmp(arg, "-v")) {
- opts.verbose_update = 1;
- continue;
- }
-
- /* "-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;
- }
+ argc = parse_options(argc, argv, unused_prefix, read_tree_options,
+ read_tree_usage, 0);
- /* "-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 (read_cache_unmerged() && (opts.prefix || opts.merge))
+ die("You need to resolve your current index first");
- 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;
- }
+ prefix_set = opts.prefix ? 1 : 0;
+ if (1 < opts.merge + opts.reset + prefix_set)
+ die("Which one? -m, --reset, or --prefix?");
+ stage = opts.merge = (opts.reset || opts.merge || prefix_set);
- /* 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 +130,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 6ec1d056e6..b771fe9b20 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 ddfdf5a3cb..95198c5de4 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-send-pack.c b/builtin-send-pack.c
index 47fb9f7baa..37e528e283 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 01bea3b583..3510a86e38 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-verify-pack.c b/builtin-verify-pack.c
index 0ee0a9af60..b5bd28e959 100644
--- a/builtin-verify-pack.c
+++ b/builtin-verify-pack.c
@@ -2,15 +2,19 @@
#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)
{
- uint32_t nr_objects, i, chain_histogram[MAX_CHAIN+1];
+ uint32_t nr_objects, i;
+ int cnt;
+ 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;
@@ -29,9 +33,11 @@ static void show_pack_info(struct packed_git *p)
&delta_chain_length,
base_sha1);
printf("%s ", sha1_to_hex(sha1));
- if (!delta_chain_length)
+ if (!delta_chain_length) {
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,
@@ -43,15 +49,21 @@ 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)
@@ -107,36 +119,31 @@ static int verify_one_pack(const char *path, int verbose)
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] <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;
+ int i;
+ const struct option verify_pack_options[] = {
+ OPT__VERBOSE(&verbose),
+ 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], verbose))
+ 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 7f7fda42f9..9f482c29f5 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 3a24ce8157..b223af416f 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/cache.h b/cache.h
index c72f125bd4..9222774e6c 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);
+
#define REFRESH_REALLY 0x0001 /* ignore_valid */
#define REFRESH_UNMERGED 0x0002 /* allow unmerged */
#define REFRESH_QUIET 0x0004 /* be quiet about it */
@@ -560,6 +563,8 @@ enum object_creation_mode {
extern enum object_creation_mode object_creation_mode;
+extern int grafts_replace_parents;
+
#define GIT_REPO_VERSION 0
extern int repository_format_version;
extern int check_repository_format(void);
diff --git a/commit.c b/commit.c
index a47fb4da27..e2bcbe8149 100644
--- a/commit.c
+++ b/commit.c
@@ -262,7 +262,11 @@ int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
bufptr[47] != '\n')
return error("bad parents in commit %s", sha1_to_hex(item->object.sha1));
bufptr += 48;
- if (graft)
+ /*
+ * The clone is shallow if nr_parent < 0, and we must
+ * not traverse its real parents even when we unhide them.
+ */
+ if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
continue;
new_parent = lookup_commit(parent);
if (new_parent)
diff --git a/compat/mingw.h b/compat/mingw.h
index c1859c5480..948de66eb5 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/config.c b/config.c
index 1682273c12..e87edeab0c 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) {
@@ -1174,7 +1172,9 @@ write_err_out:
static int section_name_match (const char *buf, const char *name)
{
int i = 0, j = 0, dot = 0;
- for (; buf[i] && buf[i] != ']'; i++) {
+ if (buf[i] != '[')
+ return 0;
+ for (i = 1; buf[i] && buf[i] != ']'; i++) {
if (!dot && isspace(buf[i])) {
dot = 1;
if (name[j++] != '.')
@@ -1195,7 +1195,17 @@ static int section_name_match (const char *buf, const char *name)
if (buf[i] != name[j++])
break;
}
- return (buf[i] == ']' && name[j] == 0);
+ if (buf[i] == ']' && name[j] == 0) {
+ /*
+ * We match, now just find the right length offset by
+ * gobbling up any whitespace after it, as well
+ */
+ i++;
+ for (; buf[i] && isspace(buf[i]); i++)
+ ; /* do nothing */
+ return i;
+ }
+ return 0;
}
/* if new_name == NULL, the section is removed instead */
@@ -1225,11 +1235,13 @@ int git_config_rename_section(const char *old_name, const char *new_name)
while (fgets(buf, sizeof(buf), config_file)) {
int i;
int length;
+ char *output = buf;
for (i = 0; buf[i] && isspace(buf[i]); i++)
; /* do nothing */
if (buf[i] == '[') {
/* it's a section */
- if (section_name_match (&buf[i+1], old_name)) {
+ int offset = section_name_match(&buf[i], old_name);
+ if (offset > 0) {
ret++;
if (new_name == NULL) {
remove = 1;
@@ -1240,14 +1252,29 @@ int git_config_rename_section(const char *old_name, const char *new_name)
ret = write_error(lock->filename);
goto out;
}
- continue;
+ /*
+ * We wrote out the new section, with
+ * a newline, now skip the old
+ * section's length
+ */
+ output += offset + i;
+ if (strlen(output) > 0) {
+ /*
+ * More content means there's
+ * a declaration to put on the
+ * next line; indent with a
+ * tab
+ */
+ output -= 1;
+ output[0] = '\t';
+ }
}
remove = 0;
}
if (remove)
continue;
- length = strlen(buf);
- if (write_in_full(out_fd, buf, length) != length) {
+ length = strlen(output);
+ if (write_in_full(out_fd, output, length) != length) {
ret = write_error(lock->filename);
goto out;
}
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 745b5fb78b..5543dc4d14 100755
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1047,6 +1047,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
"
diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el
index eace9c18eb..8c70ad8b7f 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/hg-to-git/hg-to-git.py b/contrib/hg-to-git/hg-to-git.py
index 7b03204ed1..2a6839d81e 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 1816e977b7..491e7141b4 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/entry.c b/entry.c
index d3e86c722a..f276cf3b88 100644
--- a/entry.c
+++ b/entry.c
@@ -175,6 +175,19 @@ 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.
+ */
+int check_path(const char *path, int len, struct stat *st)
+{
+ if (has_symlink_leading_path(path, len)) {
+ 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 +201,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)) {
unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
if (!changed)
return 0;
diff --git a/environment.c b/environment.c
index 720f26b67d..8f5eaa7dd8 100644
--- a/environment.c
+++ b/environment.c
@@ -47,6 +47,7 @@ enum push_default_type push_default = PUSH_DEFAULT_MATCHING;
#define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS
#endif
enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
+int grafts_replace_parents = 1;
/* Parallel index stat data preload? */
int core_preload_index = 0;
diff --git a/git-am.sh b/git-am.sh
index d64d997535..f719f6e654 100755
--- a/git-am.sh
+++ b/git-am.sh
@@ -191,6 +191,20 @@ 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
}
@@ -254,7 +268,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
}
diff --git a/git-instaweb.sh b/git-instaweb.sh
index 5f4419b69b..5f5cac75ea 100755
--- a/git-instaweb.sh
+++ b/git-instaweb.sh
@@ -256,7 +256,7 @@ apache2_conf () {
mkdir -p "$GIT_DIR/gitweb/logs"
bind=
test x"$local" = xtrue && bind='127.0.0.1:'
- echo 'text/css css' > $fqgitdir/mime.types
+ echo 'text/css css' > "$fqgitdir/mime.types"
cat > "$conf" <<EOF
ServerName "git-instaweb"
ServerRoot "$fqgitdir/gitweb"
@@ -272,13 +272,13 @@ EOF
fi
done
cat >> "$conf" <<EOF
-TypesConfig $fqgitdir/mime.types
+TypesConfig "$fqgitdir/mime.types"
DirectoryIndex gitweb.cgi
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
diff --git a/git-pull.sh b/git-pull.sh
index 4b78a0cd37..0bbd5bf7df 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--interactive.sh b/git-rebase--interactive.sh
index f96d887d23..23ded48322 100755
--- a/git-rebase--interactive.sh
+++ b/git-rebase--interactive.sh
@@ -703,7 +703,7 @@ first and then run 'git rebase --continue' again."
preserve=t
for p in $(git rev-list --parents -1 $sha1 | cut -d' ' -s -f2-)
do
- if test -f "$REWRITTEN"/$p -a \( $p != $UPSTREAM -o $sha1 = $first_after_upstream \)
+ if test -f "$REWRITTEN"/$p -a \( $p != $ONTO -o $sha1 = $first_after_upstream \)
then
preserve=f
fi
diff --git a/git-rebase.sh b/git-rebase.sh
index 18bc6946cf..3555d17a5d 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -382,8 +382,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-repack.sh b/git-repack.sh
index 1bf239499c..1eb3bca352 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -81,7 +81,7 @@ case ",$all_into_one," in
esac
args="$args $local ${GIT_QUIET:+-q} $no_reuse$extra"
-names=$(git pack-objects --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
+names=$(git pack-objects --keep-true-parents --honor-pack-keep --non-empty --all --reflog $args </dev/null "$PACKTMP") ||
exit 1
if [ -z "$names" ]; then
say Nothing new to pack.
diff --git a/git-request-pull.sh b/git-request-pull.sh
index 5917773240..630ceddf03 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -8,13 +8,33 @@ USAGE='<start> <url> [<end>]'
LONG_USAGE='Summarizes the changes between two commits to the standard output,
and includes the given URL in the generated summary.'
SUBDIRECTORY_OK='Yes'
-OPTIONS_SPEC=
+OPTIONS_SPEC='git request-pull [options] start url [end]
+--
+p show patch text as well
+'
+
. git-sh-setup
. git-parse-remote
GIT_PAGER=
export GIT_PAGER
+patch=
+while case "$#" in 0) break ;; esac
+do
+ case "$1" in
+ -p)
+ patch=-p ;;
+ --)
+ shift; break ;;
+ -*)
+ usage ;;
+ *)
+ break ;;
+ esac
+ shift
+done
+
base=$1
url=$2
head=${3-HEAD}
@@ -28,13 +48,13 @@ headrev=`git rev-parse --verify "$head"^0` || exit
merge_base=`git merge-base $baserev $headrev` ||
die "fatal: No commits in common between $base and $head"
-url=$(get_remote_url "$url")
branch=$(git ls-remote "$url" \
| sed -n -e "/^$headrev refs.heads./{
s/^.* refs.heads.//
p
q
}")
+url=$(get_remote_url "$url")
if [ -z "$branch" ]; then
echo "warn: No branch of $url is at:" >&2
git log --max-count=1 --pretty='tformat:warn: %h: %s' $headrev >&2
@@ -54,5 +74,5 @@ echo " $url $branch"
echo
git shortlog ^$baserev $headrev
-git diff -M --stat --summary $merge_base $headrev
+git diff -M --stat --summary $patch $merge_base..$headrev
exit $status
diff --git a/git-send-email.perl b/git-send-email.perl
index 8ce6f1fe57..0700d80afc 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);
@@ -654,13 +653,17 @@ if (!@to) {
}
sub expand_aliases {
- my @cur = @_;
- my @last;
- do {
- @last = @cur;
- @cur = map { $aliases{$_} ? @{$aliases{$_}} : $_ } @last;
- } while (join(',',@cur) ne join(',',@last));
- return @cur;
+ return map { expand_one_alias($_) } @_;
+}
+
+my %EXPANDED_ALIASES;
+sub expand_one_alias {
+ my $alias = shift;
+ if ($EXPANDED_ALIASES{$alias}) {
+ die "fatal: alias '$alias' expands to itself\n";
+ }
+ local $EXPANDED_ALIASES{$alias} = 1;
+ return $aliases{$alias} ? expand_aliases(@{$aliases{$alias}}) : $alias;
}
@to = expand_aliases(@to);
diff --git a/git-svn.perl b/git-svn.perl
index 9369acc4dc..1da9f0781c 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -19,6 +19,7 @@ $ENV{GIT_DIR} ||= '.git';
$Git::SVN::default_repo_id = 'svn';
$Git::SVN::default_ref_id = $ENV{GIT_SVN_ID} || 'git-svn';
$Git::SVN::Ra::_log_window_size = 100;
+$Git::SVN::_minimize_url = 'unset';
$Git::SVN::Log::TZ = $ENV{TZ};
$ENV{TZ} = 'UTC';
@@ -31,6 +32,7 @@ require SVN::Delta;
if ($SVN::Core::VERSION lt '1.1.0') {
fatal "Need SVN::Core 1.1.0 or better (got $SVN::Core::VERSION)";
}
+my $can_compress = eval { require Compress::Zlib; 1};
push @Git::SVN::Ra::ISA, 'SVN::Ra';
push @SVN::Git::Editor::ISA, 'SVN::Delta::Editor';
push @SVN::Git::Fetcher::ISA, 'SVN::Delta::Editor';
@@ -40,6 +42,7 @@ use IO::File qw//;
use File::Basename qw/dirname basename/;
use File::Path qw/mkpath/;
use File::Spec;
+use File::Find;
use Getopt::Long qw/:config gnu_getopt no_ignore_case auto_abbrev/;
use IPC::Open3;
use Git;
@@ -98,7 +101,7 @@ my %init_opts = ( 'template=s' => \$_template, 'shared:s' => \$_shared,
'trunk|T=s' => \$_trunk, 'tags|t=s@' => \@_tags,
'branches|b=s@' => \@_branches, 'prefix=s' => \$_prefix,
'stdlayout|s' => \$_stdlayout,
- 'minimize-url|m' => \$Git::SVN::_minimize_url,
+ 'minimize-url|m!' => \$Git::SVN::_minimize_url,
'no-metadata' => sub { $icv{noMetadata} = 1 },
'use-svm-props' => sub { $icv{useSvmProps} = 1 },
'use-svnsync-props' => sub { $icv{useSvnsyncProps} = 1 },
@@ -217,6 +220,10 @@ my %cmd = (
"Undo fetches back to the specified SVN revision",
{ 'revision|r=s' => \$_revision,
'parent|p' => \$_fetch_parent } ],
+ 'gc' => [ \&cmd_gc,
+ "Compress unhandled.log files in .git/svn and remove " .
+ "index files in .git/svn",
+ {} ],
);
my $cmd;
@@ -393,6 +400,10 @@ sub cmd_init {
init_subdir(@_);
do_git_init_db();
+ if ($Git::SVN::_minimize_url eq 'unset') {
+ $Git::SVN::_minimize_url = 0;
+ }
+
Git::SVN->init($url);
}
@@ -655,9 +666,22 @@ sub cmd_branch {
}
}
unless (defined $glob) {
- die "Unknown ",
- $_tag ? "tag" : "branch",
- " destination $_branch_dest\n";
+ my $dest_re = qr/\b\Q$_branch_dest\E\b/;
+ foreach my $g (@{$allglobs}) {
+ $g->{path}->{left} =~ /$dest_re/ or next;
+ if (defined $glob) {
+ die "Ambiguous destination: ",
+ $_branch_dest, "\nmatches both '",
+ $glob->{path}->{left}, "' and '",
+ $g->{path}->{left}, "'\n";
+ }
+ $glob = $g;
+ }
+ unless (defined $glob) {
+ die "Unknown ",
+ $_tag ? "tag" : "branch",
+ " destination $_branch_dest\n";
+ }
}
}
my ($lft, $rgt) = @{ $glob->{path} }{qw/left right/};
@@ -740,6 +764,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";
@@ -777,6 +802,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.
@@ -883,7 +909,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) {
@@ -1107,6 +1133,14 @@ sub cmd_reset {
print "r$r = $c ($gs->{ref_id})\n";
}
+sub cmd_gc {
+ if (!$can_compress) {
+ warn "Compress::Zlib could not be found; unhandled.log " .
+ "files will not be compressed.\n";
+ }
+ find({ wanted => \&gc_directory, no_chdir => 1}, "$ENV{GIT_DIR}/svn");
+}
+
########################### utility functions #########################
sub rebase_cmd {
@@ -1122,6 +1156,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'));
@@ -1527,6 +1572,25 @@ sub md5sum {
return $md5->hexdigest();
}
+sub gc_directory {
+ if ($can_compress && -f $_ && basename($_) eq "unhandled.log") {
+ my $out_filename = $_ . ".gz";
+ open my $in_fh, "<", $_ or die "Unable to open $_: $!\n";
+ binmode $in_fh;
+ my $gz = Compress::Zlib::gzopen($out_filename, "ab") or
+ die "Unable to open $out_filename: $!\n";
+
+ my $res;
+ while ($res = sysread($in_fh, my $str, 1024)) {
+ $gz->gzwrite($str) or
+ die "Unable to write: ".$gz->gzerror()."!\n";
+ }
+ unlink $_ or die "unlink $File::Find::name: $!\n";
+ } elsif (-f $_ && basename($_) eq "index") {
+ unlink $_ or die "unlink $_: $!\n";
+ }
+}
+
package Git::SVN;
use strict;
use warnings;
@@ -1590,23 +1654,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";
@@ -1684,33 +1748,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;
}
}
@@ -1818,9 +1884,9 @@ 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',
@@ -1895,7 +1961,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#^\./?#);
@@ -1912,16 +1978,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',
@@ -1932,7 +1998,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:
@@ -3211,7 +3277,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;
@@ -3266,12 +3332,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",
@@ -3954,7 +4032,7 @@ sub repo_path {
sub url_path {
my ($self, $path) = @_;
if ($self->{url} =~ m#^https?://#) {
- $path =~ s/([^~a-zA-Z0-9_.-])/uc sprintf("%%%02x",ord($1))/eg;
+ $path =~ s!([^~a-zA-Z0-9_./-])!uc sprintf("%%%02x",ord($1))!eg;
}
$self->{url} . '/' . $self->repo_path($path);
}
@@ -4780,7 +4858,11 @@ sub minimize_url {
my $c = '';
do {
$url .= "/$c" if length $c;
- eval { (ref $self)->new($url)->get_latest_revnum };
+ eval {
+ my $ra = (ref $self)->new($url);
+ my $latest = $ra->get_latest_revnum;
+ $ra->get_log("", $latest, 0, 1, 0, 1, sub {});
+ };
} while ($@ && ($c = shift @components));
$url;
}
@@ -5440,7 +5522,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}}->
@@ -5509,7 +5591,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 807d875ae0..4588a8bac2 100644
--- a/git.c
+++ b/git.c
@@ -416,13 +416,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/gitweb/README b/gitweb/README
index 9056d1e090..66c6a9391d 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/gitweb.perl b/gitweb/gitweb.perl
index 2cb60bedc6..be7358fdeb 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -940,10 +940,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'};
@@ -2570,7 +2573,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 +2583,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;
diff --git a/grep.h b/grep.h
index f00db0e402..28e6b2a8ec 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 6c46d8b494..294337e71c 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 a2720d576d..14d535747d 100644
--- a/http.c
+++ b/http.c
@@ -1004,7 +1004,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 +1017,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 +1033,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 +1057,8 @@ struct http_pack_request *new_http_pack_request(
abort:
free(filename);
+ free(preq->url);
+ free(preq);
return NULL;
}
@@ -1098,7 +1098,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 +1151,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 +1187,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 +1200,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 +1220,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;
}
diff --git a/ll-merge.c b/ll-merge.c
index 0571564ddf..2d6b6d6cb1 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/merge-recursive.c b/merge-recursive.c
index d415c4188d..10d7913b06 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/parse-options.c b/parse-options.c
index f7ce523a61..3b71fbb541 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -511,7 +511,7 @@ static int usage_with_options_internal(const char * const *usagestr,
continue;
pos = fprintf(stderr, " ");
- if (opts->short_name) {
+ if (opts->short_name && !(opts->flags & PARSE_OPT_NEGHELP)) {
if (opts->flags & PARSE_OPT_NODASH)
pos += fprintf(stderr, "%c", opts->short_name);
else
@@ -520,7 +520,9 @@ static int usage_with_options_internal(const char * const *usagestr,
if (opts->long_name && opts->short_name)
pos += fprintf(stderr, ", ");
if (opts->long_name)
- pos += fprintf(stderr, "--%s", opts->long_name);
+ pos += fprintf(stderr, "--%s%s",
+ (opts->flags & PARSE_OPT_NEGHELP) ? "no-" : "",
+ opts->long_name);
if (opts->type == OPTION_NUMBER)
pos += fprintf(stderr, "-NUM");
diff --git a/parse-options.h b/parse-options.h
index aba30671dc..b32587ad7c 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -36,6 +36,7 @@ enum parse_opt_option_flags {
PARSE_OPT_LASTARG_DEFAULT = 16,
PARSE_OPT_NODASH = 32,
PARSE_OPT_LITERAL_ARGHELP = 64,
+ PARSE_OPT_NEGHELP = 128,
};
struct option;
@@ -80,6 +81,9 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
* PARSE_OPT_LITERAL_ARGHELP: says that argh shouldn't be enclosed in brackets
* (i.e. '<argh>') in the help message.
* Useful for options with multiple parameters.
+ * PARSE_OPT_NEGHELP: says that the long option should always be shown with
+ * the --no prefix in the usage message. Sometimes
+ * useful for users of OPTION_NEGBIT.
*
* `callback`::
* pointer to the callback to use for OPTION_CALLBACK.
diff --git a/run-command.c b/run-command.c
index ff3d8e2d8b..f3e7abb7de 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 e345502843..0c00b25ff2 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 1d7b1b3b4f..8b3cf028ed 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/symlinks.c b/symlinks.c
index 4bdded39c5..7b0a86d357 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 bf816fc850..bd09390d32 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-cvs.sh b/t/lib-cvs.sh
new file mode 100644
index 0000000000..4b3b793730
--- /dev/null
+++ b/t/lib-cvs.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+. ./test-lib.sh
+
+unset CVS_SERVER
+# for clean cvsps cache
+HOME=$(pwd)
+export HOME
+
+if ! type cvs >/dev/null 2>&1
+then
+ say 'skipping cvsimport tests, cvs not found'
+ test_done
+fi
+
+CVS="cvs -f"
+export CVS
+
+cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
+case "$cvsps_version" in
+2.1 | 2.2*)
+ ;;
+'')
+ say 'skipping cvsimport tests, cvsps not found'
+ test_done
+ ;;
+*)
+ say 'skipping cvsimport tests, unsupported cvsps version'
+ test_done
+ ;;
+esac
+
+test_cvs_co () {
+ # Usage: test_cvs_co BRANCH_NAME
+ rm -rf module-cvs-"$1"
+ if [ "$1" = "master" ]
+ then
+ $CVS co -P -d module-cvs-"$1" -A module
+ else
+ $CVS co -P -d module-cvs-"$1" -r "$1" module
+ fi
+}
+
+test_git_co () {
+ # Usage: test_git_co BRANCH_NAME
+ (cd module-git && git checkout "$1")
+}
+
+test_cmp_branch_file () {
+ # Usage: test_cmp_branch_file BRANCH_NAME PATH
+ # The branch must already be checked out of CVS and git.
+ test_cmp module-cvs-"$1"/"$2" module-git/"$2"
+}
+
+test_cmp_branch_tree () {
+ # Usage: test_cmp_branch_tree BRANCH_NAME
+ # Check BRANCH_NAME out of CVS and git and make sure that all
+ # of the files and directories are identical.
+
+ test_cvs_co "$1" &&
+ test_git_co "$1" &&
+ (
+ cd module-cvs-"$1"
+ find . -type d -name CVS -prune -o -type f -print
+ ) | sort >module-cvs-"$1".list &&
+ (
+ cd module-git
+ find . -type d -name .git -prune -o -type f -print
+ ) | sort >module-git-"$1".list &&
+ test_cmp module-cvs-"$1".list module-git-"$1".list &&
+ cat module-cvs-"$1".list | while read f
+ do
+ test_cmp_branch_file "$1" "$f" || return 1
+ done
+}
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 5654962343..fd8631f906 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 e3d846420d..5386504790 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/t1300-repo-config.sh b/t/t1300-repo-config.sh
index 43ea283242..83b7294010 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -460,6 +460,28 @@ EOF
test_expect_success "rename succeeded" "test_cmp expect .git/config"
cat >> .git/config << EOF
+[branch "vier"] z = 1
+EOF
+
+test_expect_success "rename a section with a var on the same line" \
+ 'git config --rename-section branch.vier branch.zwei'
+
+cat > expect << EOF
+# Hallo
+ #Bello
+[branch "zwei"]
+ x = 1
+[branch "zwei"]
+ y = 1
+[branch "drei"]
+weird
+[branch "zwei"]
+ z = 1
+EOF
+
+test_expect_success "rename succeeded" "test_cmp expect .git/config"
+
+cat >> .git/config << EOF
[branch "zwei"] a = 1 [branch "vier"]
EOF
@@ -733,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/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh
index a973628e8e..4cae019521 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/t3411-rebase-preserve-around-merges.sh b/t/t3411-rebase-preserve-around-merges.sh
index 6533505218..14a23cd872 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
new file mode 100755
index 0000000000..ee0a6cccfd
--- /dev/null
+++ b/t/t3414-rebase-preserve-onto.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Greg Price
+#
+
+test_description='git rebase -p should respect --onto
+
+In a rebase with --onto, we should rewrite all the commits that
+aren'"'"'t on top of $ONTO, even if they are on top of $UPSTREAM.
+'
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+# Set up branches like this:
+# A1---B1---E1---F1---G1
+# \ \ /
+# \ \--C1---D1--/
+# H1
+
+test_expect_success 'setup' '
+ test_commit A1 &&
+ test_commit B1 &&
+ test_commit C1 &&
+ test_commit D1 &&
+ git reset --hard B1 &&
+ test_commit E1 &&
+ test_commit F1 &&
+ test_merge G1 D1 &&
+ git reset --hard A1 &&
+ test_commit H1
+'
+
+# Now rebase merge G1 from both branches' base B1, both should move:
+# A1---B1---E1---F1---G1
+# \ \ /
+# \ \--C1---D1--/
+# \
+# H1---E2---F2---G2
+# \ /
+# \--C2---D2--/
+
+test_expect_success 'rebase from B1 onto H1' '
+ git checkout G1 &&
+ git rebase -p --onto H1 B1 &&
+ test "$(git rev-parse HEAD^1^1^1)" = "$(git rev-parse H1)" &&
+ test "$(git rev-parse HEAD^2^1^1)" = "$(git rev-parse H1)"
+'
+
+# On the other hand if rebase from E1 which is within one branch,
+# then the other branch stays:
+# A1---B1---E1---F1---G1
+# \ \ /
+# \ \--C1---D1--/
+# \ \
+# H1-----F3-----G3
+
+test_expect_success 'rebase from E1 onto H1' '
+ git checkout G1 &&
+ git rebase -p --onto H1 E1 &&
+ test "$(git rev-parse HEAD^1^1)" = "$(git rev-parse H1)" &&
+ test "$(git rev-parse HEAD^2)" = "$(git rev-parse D1)"
+'
+
+# And the same if we rebase from a commit in the second-parent branch.
+# A1---B1---E1---F1----G1
+# \ \ \ /
+# \ \--C1---D1-\-/
+# \ \
+# H1------D3------G4
+
+test_expect_success 'rebase from C1 onto H1' '
+ git checkout G1 &&
+ git rev-list --first-parent --pretty=oneline C1..G1 &&
+ git rebase -p --onto H1 C1 &&
+ test "$(git rev-parse HEAD^2^1)" = "$(git rev-parse H1)" &&
+ test "$(git rev-parse HEAD^1)" = "$(git rev-parse F1)"
+'
+
+test_done
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 922a8941ed..531f5b795c 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 4ea42e00da..a7602cf923 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/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index 5698a9a736..fac2093d7f 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -154,7 +154,9 @@ create_patch () {
index e69de29..8bd6648 100644
--- a/target
+++ b/target
- @@ -0,0 +1 @@
+ @@ -0,0 +1,3 @@
+ +An empty line follows
+ +
+A line with trailing whitespace and no newline_
\ No newline at end of file
EOF
@@ -162,8 +164,10 @@ create_patch () {
test_expect_success 'trailing whitespace & no newline at the end of file' '
>target &&
- create_patch | git apply --whitespace=fix - &&
- grep "newline$" target
+ create_patch >patch-file &&
+ git apply --whitespace=fix patch-file &&
+ grep "newline$" target &&
+ grep "^$" target
'
test_done
diff --git a/t/t4132-apply-removal.sh b/t/t4132-apply-removal.sh
new file mode 100755
index 0000000000..bb1ffe3b6c
--- /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 a12bf84623..8296605234 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/t5510-fetch.sh b/t/t5510-fetch.sh
index bee3424fed..d13c806624 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 c5a2e66a09..dd2ee842e0 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 f5102b902a..a696b8791b 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/t6010-merge-base.sh b/t/t6010-merge-base.sh
index 04e4b7c5c2..0144d9e858 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/t6020-merge-df.sh b/t/t6020-merge-df.sh
index a19d49de28..e71c687f2b 100755
--- a/t/t6020-merge-df.sh
+++ b/t/t6020-merge-df.sh
@@ -22,4 +22,27 @@ git commit -m "File: dir"'
test_expect_code 1 'Merge with d/f conflicts' 'git merge "merge msg" B master'
+test_expect_failure 'F/D conflict' '
+ git reset --hard &&
+ git checkout master &&
+ rm .git/index &&
+
+ mkdir before &&
+ echo FILE >before/one &&
+ echo FILE >after &&
+ git add . &&
+ git commit -m first &&
+
+ rm -f after &&
+ git mv before after &&
+ git commit -m move &&
+
+ git checkout -b para HEAD^ &&
+ echo COMPLETELY ANOTHER FILE >another &&
+ git add . &&
+ git commit -m para &&
+
+ git merge master
+'
+
test_done
diff --git a/t/t6035-merge-dir-to-symlink.sh b/t/t6035-merge-dir-to-symlink.sh
new file mode 100755
index 0000000000..5b96fb0b37
--- /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 0000000000..b874141658
--- /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/t7002-grep.sh b/t/t7002-grep.sh
index b13aa7e89a..b4709e28b5 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/t7700-repack.sh b/t/t7700-repack.sh
index 87c9b0e121..f4aa054750 100755
--- a/t/t7700-repack.sh
+++ b/t/t7700-repack.sh
@@ -149,5 +149,17 @@ test_expect_success 'local packed unreachable obs that exist in alternate ODB ar
test_must_fail git show $csha1
'
+test_expect_success 'objects made unreachable by grafts only are kept' '
+ test_tick &&
+ git commit --allow-empty -m "commit 4" &&
+ H0=$(git rev-parse HEAD) &&
+ H1=$(git rev-parse HEAD^) &&
+ H2=$(git rev-parse HEAD^^) &&
+ echo "$H0 $H2" > .git/info/grafts &&
+ git reflog expire --expire=now --expire-unreachable=now --all &&
+ git repack -a -d &&
+ git cat-file -t $H1
+ '
+
test_done
diff --git a/t/t8005-blame-i18n.sh b/t/t8005-blame-i18n.sh
index 9cca14d080..cb390559f9 100755
--- a/t/t8005-blame-i18n.sh
+++ b/t/t8005-blame-i18n.sh
@@ -4,7 +4,7 @@ test_description='git blame encoding conversion'
. ./test-lib.sh
. "$TEST_DIRECTORY"/t8005/utf8.txt
-. "$TEST_DIRECTORY"/t8005/iso8859-5.txt
+. "$TEST_DIRECTORY"/t8005/euc-japan.txt
. "$TEST_DIRECTORY"/t8005/sjis.txt
test_expect_success 'setup the repository' '
@@ -13,10 +13,10 @@ test_expect_success 'setup the repository' '
git add file &&
git commit --author "$UTF8_NAME <utf8@localhost>" -m "$UTF8_MSG" &&
- echo "ISO-8859-5 LINE" >> file &&
+ echo "EUC-JAPAN LINE" >> file &&
git add file &&
- git config i18n.commitencoding ISO8859-5 &&
- git commit --author "$ISO8859_5_NAME <iso8859-5@localhost>" -m "$ISO8859_5_MSG" &&
+ git config i18n.commitencoding eucJP &&
+ git commit --author "$EUC_JAPAN_NAME <euc-japan@localhost>" -m "$EUC_JAPAN_MSG" &&
echo "SJIS LINE" >> file &&
git add file &&
@@ -41,17 +41,17 @@ test_expect_success \
'
cat >expected <<EOF
-author $ISO8859_5_NAME
-summary $ISO8859_5_MSG
-author $ISO8859_5_NAME
-summary $ISO8859_5_MSG
-author $ISO8859_5_NAME
-summary $ISO8859_5_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
EOF
test_expect_success \
'blame respects i18n.logoutputencoding' '
- git config i18n.logoutputencoding ISO8859-5 &&
+ git config i18n.logoutputencoding eucJP &&
git blame --incremental file | \
egrep "^(author|summary) " > actual &&
test_cmp actual expected
@@ -76,8 +76,8 @@ test_expect_success \
cat >expected <<EOF
author $SJIS_NAME
summary $SJIS_MSG
-author $ISO8859_5_NAME
-summary $ISO8859_5_MSG
+author $EUC_JAPAN_NAME
+summary $EUC_JAPAN_MSG
author $UTF8_NAME
summary $UTF8_MSG
EOF
diff --git a/t/t8005/euc-japan.txt b/t/t8005/euc-japan.txt
new file mode 100644
index 0000000000..288f040c99
--- /dev/null
+++ b/t/t8005/euc-japan.txt
@@ -0,0 +1,2 @@
+EUC_JAPAN_NAME="»³ÅÄ ÂÀϺ"
+EUC_JAPAN_MSG="¥Ö¥ì¡¼¥à¤Î¥Æ¥¹¥È¤Ç¤¹¡£"
diff --git a/t/t8005/sjis.txt b/t/t8005/sjis.txt
index 2ccfbad207..bbdefeaced 100644
--- a/t/t8005/sjis.txt
+++ b/t/t8005/sjis.txt
@@ -1,2 +1,2 @@
-SJIS_NAME="„I„r„p„~ „P„u„„„‚„€„r„y„‰ „R„y„t„€„‚„€„r"
-SJIS_MSG="„S„u„ƒ„„„€„r„€„u „ƒ„€„€„q„‹„u„~„y„u"
+SJIS_NAME="ŽR“c ‘¾˜Y"
+SJIS_MSG="ƒuƒŒ[ƒ€‚̃eƒXƒg‚Å‚·B"
diff --git a/t/t8005/utf8.txt b/t/t8005/utf8.txt
index f46cfc56d8..4d00dbea76 100644
--- a/t/t8005/utf8.txt
+++ b/t/t8005/utf8.txt
@@ -1,2 +1,2 @@
-UTF8_NAME="Иван Петрович Сидоров"
-UTF8_MSG="ТеÑтовое Ñообщение"
+UTF8_NAME="山田 太郎"
+UTF8_MSG="ブレームã®ãƒ†ã‚¹ãƒˆã§ã™ã€‚"
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 9da4178c94..929499e996 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 78610b61e6..bbfd7f4793 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 3a9e07768d..901b8e09fb 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/t9135-git-svn-moved-branch-empty-file.sh b/t/t9135-git-svn-moved-branch-empty-file.sh
index 03705fa4ce..5280e5f1e4 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/t9142-git-svn-shallow-clone.sh b/t/t9142-git-svn-shallow-clone.sh
index fd5ad49471..1236accd99 100755
--- a/t/t9142-git-svn-shallow-clone.sh
+++ b/t/t9142-git-svn-shallow-clone.sh
@@ -27,4 +27,6 @@ test_expect_success 'clone trunk with "-r HEAD"' '
( cd g && git rev-parse --symbolic --verify HEAD )
'
+stop_httpd
+
test_done
diff --git a/t/t9143-git-svn-gc.sh b/t/t9143-git-svn-gc.sh
new file mode 100755
index 0000000000..99f69c6a0b
--- /dev/null
+++ b/t/t9143-git-svn-gc.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Robert Allan Zeh
+
+test_description='git svn gc basic tests'
+
+. ./lib-git-svn.sh
+
+test_expect_success 'setup directories and test repo' '
+ mkdir import &&
+ mkdir tmp &&
+ echo "Sample text for Subversion repository." > import/test.txt &&
+ svn_cmd import -m "import for git svn" import "$svnrepo" > /dev/null
+ '
+
+test_expect_success 'checkout working copy from svn' \
+ 'svn_cmd co "$svnrepo" test_wc'
+
+test_expect_success 'set some properties to create an unhandled.log file' '
+ (
+ cd test_wc &&
+ svn_cmd propset foo bar test.txt &&
+ svn_cmd commit -m "property set"
+ )'
+
+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/refs/remotes/git-svn/unhandled.log tmp
+ '
+
+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/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/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/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 0000000000..7600a35cd4
--- /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 0000000000..16852d26ae
--- /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/t9301-fast-export.sh b/t/t9301-fast-export.sh
index 8c8a9e63c2..356964e53a 100755
--- a/t/t9301-fast-export.sh
+++ b/t/t9301-fast-export.sh
@@ -262,6 +262,94 @@ test_expect_success 'cope with tagger-less tags' '
'
+test_expect_success 'setup for limiting exports by PATH' '
+ mkdir limit-by-paths &&
+ cd limit-by-paths &&
+ git init &&
+ echo hi > there &&
+ git add there &&
+ git commit -m "First file" &&
+ echo foo > bar &&
+ git add bar &&
+ git commit -m "Second file" &&
+ git tag -a -m msg mytag &&
+ echo morefoo >> bar &&
+ git add bar &&
+ git commit -m "Change to second file" &&
+ cd ..
+'
+
+cat > limit-by-paths/expected << EOF
+blob
+mark :1
+data 3
+hi
+
+reset refs/tags/mytag
+commit refs/tags/mytag
+mark :2
+author A U Thor <author@example.com> 1112912713 -0700
+committer C O Mitter <committer@example.com> 1112912713 -0700
+data 11
+First file
+M 100644 :1 there
+
+EOF
+
+test_expect_success 'dropping tag of filtered out object' '
+ cd limit-by-paths &&
+ git fast-export --tag-of-filtered-object=drop mytag -- there > output &&
+ test_cmp output expected &&
+ cd ..
+'
+
+cat >> limit-by-paths/expected << EOF
+tag mytag
+from :2
+tagger C O Mitter <committer@example.com> 1112912713 -0700
+data 4
+msg
+
+EOF
+
+test_expect_success 'rewriting tag of filtered out object' '
+ cd limit-by-paths &&
+ git fast-export --tag-of-filtered-object=rewrite mytag -- there > output &&
+ test_cmp output expected &&
+ cd ..
+'
+
+cat > limit-by-paths/expected << EOF
+blob
+mark :1
+data 4
+foo
+
+blob
+mark :2
+data 3
+hi
+
+reset refs/heads/master
+commit refs/heads/master
+mark :3
+author A U Thor <author@example.com> 1112912713 -0700
+committer C O Mitter <committer@example.com> 1112912713 -0700
+data 12
+Second file
+M 100644 :1 bar
+M 100644 :2 there
+
+EOF
+
+test_expect_failure 'no exact-ref revisions included' '
+ cd limit-by-paths &&
+ git fast-export master~2..master~1 > output &&
+ test_cmp output expected &&
+ cd ..
+'
+
+
test_expect_success 'set-up a few more tags for tag export tests' '
git checkout -f master &&
HEAD_TREE=`git show -s --pretty=raw HEAD | grep tree | sed "s/tree //"` &&
@@ -271,8 +359,14 @@ test_expect_success 'set-up a few more tags for tag export tests' '
git tag -a tag-obj_tag-obj -m "tagging a tag" tree_tag-obj
'
+test_expect_success 'tree_tag' '
+ mkdir result &&
+ (cd result && git init) &&
+ git fast-export tree_tag > fe-stream &&
+ (cd result && git fast-import < ../fe-stream)
+'
+
# NEEDSWORK: not just check return status, but validate the output
-test_expect_success 'tree_tag' 'git fast-export tree_tag'
test_expect_success 'tree_tag-obj' 'git fast-export tree_tag-obj'
test_expect_success 'tag-obj_tag' 'git fast-export tag-obj_tag'
test_expect_success 'tag-obj_tag-obj' 'git fast-export tag-obj_tag-obj'
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 4322a0c1ed..363345faef 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -1,7 +1,7 @@
#!/bin/sh
test_description='git cvsimport basic tests'
-. ./test-lib.sh
+. ./lib-cvs.sh
if ! test_have_prereq PERL; then
say 'skipping git cvsimport tests, perl not available'
@@ -10,37 +10,13 @@ fi
CVSROOT=$(pwd)/cvsroot
export CVSROOT
-unset CVS_SERVER
-# for clean cvsps cache
-HOME=$(pwd)
-export HOME
-
-if ! type cvs >/dev/null 2>&1
-then
- say 'skipping cvsimport tests, cvs not found'
- test_done
-fi
-
-cvsps_version=`cvsps -h 2>&1 | sed -ne 's/cvsps version //p'`
-case "$cvsps_version" in
-2.1 | 2.2*)
- ;;
-'')
- say 'skipping cvsimport tests, cvsps not found'
- test_done
- ;;
-*)
- say 'skipping cvsimport tests, unsupported cvsps version'
- test_done
- ;;
-esac
-test_expect_success 'setup cvsroot' 'cvs init'
+test_expect_success 'setup cvsroot' '$CVS init'
test_expect_success 'setup a cvs module' '
mkdir "$CVSROOT/module" &&
- cvs co -d module-cvs module &&
+ $CVS co -d module-cvs module &&
cd module-cvs &&
cat <<EOF >o_fortuna &&
O Fortuna
@@ -59,13 +35,13 @@ egestatem,
potestatem
dissolvit ut glaciem.
EOF
- cvs add o_fortuna &&
+ $CVS add o_fortuna &&
cat <<EOF >message &&
add "O Fortuna" lyrics
These public domain lyrics make an excellent sample text.
EOF
- cvs commit -F message &&
+ $CVS commit -F message &&
cd ..
'
@@ -103,7 +79,7 @@ translate to English
My Latin is terrible.
EOF
- cvs commit -F message &&
+ $CVS commit -F message &&
cd ..
'
@@ -121,8 +97,8 @@ test_expect_success 'update cvs module' '
cd module-cvs &&
echo 1 >tick &&
- cvs add tick &&
- cvs commit -m 1
+ $CVS add tick &&
+ $CVS commit -m 1
cd ..
'
@@ -140,7 +116,7 @@ test_expect_success 'cvsimport.module config works' '
test_expect_success 'import from a CVS working tree' '
- cvs co -d import-from-wt module &&
+ $CVS co -d import-from-wt module &&
cd import-from-wt &&
git cvsimport -a -z0 &&
echo 1 >expect &&
@@ -150,4 +126,6 @@ test_expect_success 'import from a CVS working tree' '
'
+test_expect_success 'test entire HEAD' 'test_cmp_branch_tree master'
+
test_done
diff --git a/t/t9601-cvsimport-vendor-branch.sh b/t/t9601-cvsimport-vendor-branch.sh
new file mode 100755
index 0000000000..3afaf56526
--- /dev/null
+++ b/t/t9601-cvsimport-vendor-branch.sh
@@ -0,0 +1,86 @@
+#!/bin/sh
+
+# Description of the files in the repository:
+#
+# imported-once.txt:
+#
+# Imported once. 1.1 and 1.1.1.1 should be identical.
+#
+# imported-twice.txt:
+#
+# Imported twice. HEAD should reflect the contents of the
+# second import (i.e., have the same contents as 1.1.1.2).
+#
+# imported-modified.txt:
+#
+# Imported, then modified on HEAD. HEAD should reflect the
+# modification.
+#
+# imported-modified-imported.txt:
+#
+# Imported, then modified on HEAD, then imported again.
+#
+# added-imported.txt,v:
+#
+# Added with 'cvs add' to create 1.1, then imported with
+# completely different contents to create 1.1.1.1, therefore the
+# vendor branch was never the default branch.
+#
+# imported-anonymously.txt:
+#
+# Like imported-twice.txt, but with a vendor branch whose branch
+# tag has been removed.
+
+test_description='git cvsimport handling of vendor branches'
+. ./lib-cvs.sh
+
+CVSROOT="$TEST_DIRECTORY"/t9601/cvsroot
+export CVSROOT
+
+test_expect_success 'import a module with a vendor branch' '
+
+ git cvsimport -C module-git module
+
+'
+
+test_expect_success 'check HEAD out of cvs repository' 'test_cvs_co master'
+
+test_expect_success 'check master out of git repository' 'test_git_co master'
+
+test_expect_success 'check a file that was imported once' '
+
+ test_cmp_branch_file master imported-once.txt
+
+'
+
+test_expect_failure 'check a file that was imported twice' '
+
+ test_cmp_branch_file master imported-twice.txt
+
+'
+
+test_expect_success 'check a file that was imported then modified on HEAD' '
+
+ test_cmp_branch_file master imported-modified.txt
+
+'
+
+test_expect_success 'check a file that was imported, modified, then imported again' '
+
+ test_cmp_branch_file master imported-modified-imported.txt
+
+'
+
+test_expect_success 'check a file that was added to HEAD then imported' '
+
+ test_cmp_branch_file master added-imported.txt
+
+'
+
+test_expect_success 'a vendor branch whose tag has been removed' '
+
+ test_cmp_branch_file master imported-anonymously.txt
+
+'
+
+test_done
diff --git a/t/t9601/cvsroot/.gitattributes b/t/t9601/cvsroot/.gitattributes
new file mode 100644
index 0000000000..562b12e16e
--- /dev/null
+++ b/t/t9601/cvsroot/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9601/cvsroot/CVSROOT/.gitignore b/t/t9601/cvsroot/CVSROOT/.gitignore
new file mode 100644
index 0000000000..3bb9b34173
--- /dev/null
+++ b/t/t9601/cvsroot/CVSROOT/.gitignore
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9601/cvsroot/module/added-imported.txt,v b/t/t9601/cvsroot/module/added-imported.txt,v
new file mode 100644
index 0000000000..5f83072ea4
--- /dev/null
+++ b/t/t9601/cvsroot/module/added-imported.txt,v
@@ -0,0 +1,44 @@
+head 1.1;
+access;
+symbols
+ vtag-4:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2004.02.09.15.43.15; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.16; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Add a file to the working copy.
+@
+text
+@Adding this file, before importing it with different contents.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-4).
+@
+text
+@d1 1
+a1 1
+This is vtag-4 (on vbranchA) of added-then-imported.txt.
+@
+
diff --git a/t/t9601/cvsroot/module/imported-anonymously.txt,v b/t/t9601/cvsroot/module/imported-anonymously.txt,v
new file mode 100644
index 0000000000..55e1b0ca5d
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-anonymously.txt,v
@@ -0,0 +1,42 @@
+head 1.1;
+branch 1.1.1;
+access;
+symbols
+ vtag-1:1.1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@This is vtag-1 (on vbranchA) of imported-anonymously.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
diff --git a/t/t9601/cvsroot/module/imported-modified-imported.txt,v b/t/t9601/cvsroot/module/imported-modified-imported.txt,v
new file mode 100644
index 0000000000..e5830aeb37
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-modified-imported.txt,v
@@ -0,0 +1,76 @@
+head 1.2;
+access;
+symbols
+ vtag-2:1.1.1.2
+ vtag-1:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2004.02.09.15.43.14; author kfogel; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next 1.1.1.2;
+
+1.1.1.2
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@First regular commit, to imported-modified-imported.txt, on HEAD.
+@
+text
+@This is a modification of imported-modified-imported.txt on HEAD.
+It should supersede the version from the vendor branch.
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d1 2
+a2 1
+This is vtag-1 (on vbranchA) of imported-modified-imported.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
+1.1.1.2
+log
+@Import (vbranchA, vtag-2).
+@
+text
+@d1 1
+a1 1
+This is vtag-2 (on vbranchA) of imported-modified-imported.txt.
+@
+
+
diff --git a/t/t9601/cvsroot/module/imported-modified.txt,v b/t/t9601/cvsroot/module/imported-modified.txt,v
new file mode 100644
index 0000000000..bbcfe447b9
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-modified.txt,v
@@ -0,0 +1,59 @@
+head 1.2;
+access;
+symbols
+ vtag-1:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2004.02.09.15.43.14; author kfogel; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Commit on HEAD.
+@
+text
+@This is a modification of imported-modified.txt on HEAD.
+It should supersede the version from the vendor branch.
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d1 2
+a2 1
+This is vtag-1 (on vbranchA) of imported-modified.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
diff --git a/t/t9601/cvsroot/module/imported-once.txt,v b/t/t9601/cvsroot/module/imported-once.txt,v
new file mode 100644
index 0000000000..c5dd82b12d
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-once.txt,v
@@ -0,0 +1,43 @@
+head 1.1;
+branch 1.1.1;
+access;
+symbols
+ vtag-1:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@This is vtag-1 (on vbranchA) of imported-once.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
diff --git a/t/t9601/cvsroot/module/imported-twice.txt,v b/t/t9601/cvsroot/module/imported-twice.txt,v
new file mode 100644
index 0000000000..d1f3f1b344
--- /dev/null
+++ b/t/t9601/cvsroot/module/imported-twice.txt,v
@@ -0,0 +1,60 @@
+head 1.1;
+branch 1.1.1;
+access;
+symbols
+ vtag-2:1.1.1.2
+ vtag-1:1.1.1.1
+ vbranchA:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next 1.1.1.2;
+
+1.1.1.2
+date 2004.02.09.15.43.13; author kfogel; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@This is vtag-1 (on vbranchA) of imported-twice.txt.
+@
+
+
+1.1.1.1
+log
+@Import (vbranchA, vtag-1).
+@
+text
+@@
+
+
+1.1.1.2
+log
+@Import (vbranchA, vtag-2).
+@
+text
+@d1 1
+a1 1
+This is vtag-2 (on vbranchA) of imported-twice.txt.
+@
+
+
diff --git a/t/t9602-cvsimport-branches-tags.sh b/t/t9602-cvsimport-branches-tags.sh
new file mode 100755
index 0000000000..67878b2d0c
--- /dev/null
+++ b/t/t9602-cvsimport-branches-tags.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+# A description of the repository used for this test can be found in
+# t9602/README.
+
+test_description='git cvsimport handling of branches and tags'
+. ./lib-cvs.sh
+
+CVSROOT="$TEST_DIRECTORY"/t9602/cvsroot
+export CVSROOT
+
+test_expect_success 'import module' '
+
+ git cvsimport -C module-git module
+
+'
+
+test_expect_success 'test branch master' '
+
+ test_cmp_branch_tree master
+
+'
+
+test_expect_success 'test branch vendorbranch' '
+
+ test_cmp_branch_tree vendorbranch
+
+'
+
+test_expect_failure 'test branch B_FROM_INITIALS' '
+
+ test_cmp_branch_tree B_FROM_INITIALS
+
+'
+
+test_expect_failure 'test branch B_FROM_INITIALS_BUT_ONE' '
+
+ test_cmp_branch_tree B_FROM_INITIALS_BUT_ONE
+
+'
+
+test_expect_failure 'test branch B_MIXED' '
+
+ test_cmp_branch_tree B_MIXED
+
+'
+
+test_expect_success 'test branch B_SPLIT' '
+
+ test_cmp_branch_tree B_SPLIT
+
+'
+
+test_expect_failure 'test tag vendortag' '
+
+ test_cmp_branch_tree vendortag
+
+'
+
+test_expect_success 'test tag T_ALL_INITIAL_FILES' '
+
+ test_cmp_branch_tree T_ALL_INITIAL_FILES
+
+'
+
+test_expect_failure 'test tag T_ALL_INITIAL_FILES_BUT_ONE' '
+
+ test_cmp_branch_tree T_ALL_INITIAL_FILES_BUT_ONE
+
+'
+
+test_expect_failure 'test tag T_MIXED' '
+
+ test_cmp_branch_tree T_MIXED
+
+'
+
+
+test_done
diff --git a/t/t9602/README b/t/t9602/README
new file mode 100644
index 0000000000..c231e0f26f
--- /dev/null
+++ b/t/t9602/README
@@ -0,0 +1,62 @@
+This repository is for testing the ability to group revisions
+correctly along tags and branches. Here is its history:
+
+ 1. The initial import (revision 1.1 of everybody) created a
+ directory structure with a file named `default' in each dir:
+
+ ./
+ default
+ sub1/default
+ subsubA/default
+ subsubB/default
+ sub2/default
+ subsubA/default
+ sub3/default
+
+ 2. Then tagged everyone with T_ALL_INITIAL_FILES.
+
+ 3. Then tagged everyone except sub1/subsubB/default with
+ T_ALL_INITIAL_FILES_BUT_ONE.
+
+ 4. Then created branch B_FROM_INITIALS on everyone.
+
+ 5. Then created branch B_FROM_INITIALS_BUT_ONE on everyone except
+ /sub1/subsubB/default.
+
+ 6. Then committed modifications to two files: sub3/default, and
+ sub1/subsubA/default.
+
+ 7. Then committed a modification to all 7 files.
+
+ 8. Then backdated sub3/default to revision 1.2, and
+ sub2/subsubA/default to revision 1.1, and tagged with T_MIXED.
+
+ 9. Same as 8, but tagged with -b to create branch B_MIXED.
+
+ 10. Switched the working copy to B_MIXED, and added
+ sub2/branch_B_MIXED_only. (That's why the RCS file is in
+ sub2/Attic/ -- it never existed on trunk.)
+
+ 11. In one commit, modified default, sub1/default, and
+ sub2/subsubA/default, on branch B_MIXED.
+
+ 12. Did "cvs up -A" on sub2/default, then in one commit, made a
+ change to sub2/default and sub2/branch_B_MIXED_only. So this
+ commit should be spread between the branch and the trunk.
+
+ 13. Do "cvs up -A" to get everyone back to trunk, then make a new
+ branch B_SPLIT on everyone except sub1/subsubB/default,v.
+
+ 14. Switch to branch B_SPLIT (see sub1/subsubB/default disappear)
+ and commit a change that affects everyone except sub3/default.
+
+ 15. An hour or so later, "cvs up -A" to get sub1/subsubB/default
+ back, then commit a change on that file, on trunk. (It's
+ important that this change happened after the previous commits
+ on B_SPLIT.)
+
+ 16. Branch sub1/subsubB/default to B_SPLIT, then "cvs up -r B_SPLIT"
+ to switch the whole working copy to the branch.
+
+ 17. Commit a change on B_SPLIT, to sub1/subsubB/default and
+ sub3/default.
diff --git a/t/t9602/cvsroot/.gitattributes b/t/t9602/cvsroot/.gitattributes
new file mode 100644
index 0000000000..562b12e16e
--- /dev/null
+++ b/t/t9602/cvsroot/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9602/cvsroot/CVSROOT/.gitignore b/t/t9602/cvsroot/CVSROOT/.gitignore
new file mode 100644
index 0000000000..3bb9b34173
--- /dev/null
+++ b/t/t9602/cvsroot/CVSROOT/.gitignore
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9602/cvsroot/module/default,v b/t/t9602/cvsroot/module/default,v
new file mode 100644
index 0000000000..3b68382a3b
--- /dev/null
+++ b/t/t9602/cvsroot/module/default,v
@@ -0,0 +1,102 @@
+head 1.2;
+access;
+symbols
+ B_SPLIT:1.2.0.4
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.2.2.1
+ 1.2.4.1;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2003.05.23.00.31.36; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.4.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is the file `default' in the top level of the project.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.2.4.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a5 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2.2.1
+log
+@Modify three files, on branch B_MIXED.
+@
+text
+@a5 2
+
+This line was added on branch B_MIXED only (affecting 3 files).
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub1/default,v b/t/t9602/cvsroot/module/sub1/default,v
new file mode 100644
index 0000000000..b7fdccdfdf
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub1/default,v
@@ -0,0 +1,102 @@
+head 1.2;
+access;
+symbols
+ B_SPLIT:1.2.0.4
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.2.2.1
+ 1.2.4.1;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2003.05.23.00.31.36; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.4.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub1/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.2.4.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a5 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2.2.1
+log
+@Modify three files, on branch B_MIXED.
+@
+text
+@a5 2
+
+This line was added on branch B_MIXED only (affecting 3 files).
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub1/subsubA/default,v b/t/t9602/cvsroot/module/sub1/subsubA/default,v
new file mode 100644
index 0000000000..472b7b2bd9
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub1/subsubA/default,v
@@ -0,0 +1,101 @@
+head 1.3;
+access;
+symbols
+ B_SPLIT:1.3.0.4
+ B_MIXED:1.3.0.2
+ T_MIXED:1.3
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.3.4.1;
+next 1.2;
+
+1.2
+date 2003.05.23.00.15.26; author jrandom; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.3.4.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub1/subsubA/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added by the first commit (affecting two files).
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.3.4.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a7 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2
+log
+@First commit to proj, affecting two files.
+@
+text
+@d6 2
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub1/subsubB/default,v b/t/t9602/cvsroot/module/sub1/subsubB/default,v
new file mode 100644
index 0000000000..fe6efa4554
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub1/subsubB/default,v
@@ -0,0 +1,107 @@
+head 1.3;
+access;
+symbols
+ B_SPLIT:1.3.0.2
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2003.06.03.04.29.14; author jrandom; state Exp;
+branches
+ 1.3.2.1;
+next 1.2;
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.3.2.1
+date 2003.06.03.04.33.13; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@A trunk change to sub1/subsubB/default. This was committed about an
+hour after an earlier change that affected most files on branch
+B_SPLIT. This file is not on that branch yet, but after this commit,
+we'll branch to B_SPLIT, albeit rooted in a revision that didn't exist
+at the time the rest of B_SPLIT was created.
+@
+text
+@This is sub1/subsubB/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+
+This bit was committed on trunk about an hour after an earlier change
+to everyone else on branch B_SPLIT. Afterwards, we'll finally branch
+this file to B_SPLIT, but rooted in a revision that didn't exist at
+the time the rest of B_SPLIT was created.
+@
+
+
+1.3.2.1
+log
+@This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT. Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+text
+@a10 4
+
+This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT. Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@d6 5
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v b/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v
new file mode 100644
index 0000000000..34c9789f2f
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v
@@ -0,0 +1,59 @@
+head 1.1;
+access;
+symbols
+ B_MIXED:1.1.0.2;
+locks; strict;
+comment @# @;
+
+
+1.1
+date 2003.05.23.00.25.26; author jrandom; state dead;
+branches
+ 1.1.2.1;
+next ;
+
+1.1.2.1
+date 2003.05.23.00.25.26; author jrandom; state Exp;
+branches;
+next 1.1.2.2;
+
+1.1.2.2
+date 2003.05.23.00.48.51; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.1
+log
+@file branch_B_MIXED_only was initially added on branch B_MIXED.
+@
+text
+@@
+
+
+1.1.2.1
+log
+@Add a file on branch B_MIXED.
+@
+text
+@a0 1
+This file was added on branch B_MIXED. It never existed on trunk.
+@
+
+
+1.1.2.2
+log
+@A single commit affecting one file on branch B_MIXED and one on trunk.
+@
+text
+@a1 3
+
+The same commit added these two lines here on branch B_MIXED, and two
+similar lines to ./default on trunk.
+@
+
+
diff --git a/t/t9602/cvsroot/module/sub2/default,v b/t/t9602/cvsroot/module/sub2/default,v
new file mode 100644
index 0000000000..018f7f8ece
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub2/default,v
@@ -0,0 +1,102 @@
+head 1.3;
+access;
+symbols
+ B_SPLIT:1.3.0.2
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2003.05.23.00.48.51; author jrandom; state Exp;
+branches
+ 1.3.2.1;
+next 1.2;
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.3.2.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@A single commit affecting one file on branch B_MIXED and one on trunk.
+@
+text
+@This is sub2/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+
+The same commit added these two lines here on trunk, and two similar
+lines to ./branch_B_MIXED_only on branch B_MIXED.
+@
+
+
+1.3.2.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a8 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@d6 3
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub2/subsubA/default,v b/t/t9602/cvsroot/module/sub2/subsubA/default,v
new file mode 100644
index 0000000000..d13242cb09
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub2/subsubA/default,v
@@ -0,0 +1,102 @@
+head 1.2;
+access;
+symbols
+ B_SPLIT:1.2.0.2
+ B_MIXED:1.1.0.2
+ T_MIXED:1.1
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.2.2.1;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1
+ 1.1.2.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.1.2.1
+date 2003.05.23.00.31.36; author jrandom; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2003.06.03.03.20.31; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub2/subsub2/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.2.2.1
+log
+@First change on branch B_SPLIT.
+
+This change excludes sub3/default, because it was not part of this
+commit, and sub1/subsubB/default, which is not even on the branch yet.
+@
+text
+@a5 2
+
+First change on branch B_SPLIT.
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.2.1
+log
+@Modify three files, on branch B_MIXED.
+@
+text
+@a3 2
+
+This line was added on branch B_MIXED only (affecting 3 files).
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9602/cvsroot/module/sub3/default,v b/t/t9602/cvsroot/module/sub3/default,v
new file mode 100644
index 0000000000..88e4567434
--- /dev/null
+++ b/t/t9602/cvsroot/module/sub3/default,v
@@ -0,0 +1,102 @@
+head 1.3;
+access;
+symbols
+ B_SPLIT:1.3.0.2
+ B_MIXED:1.2.0.2
+ T_MIXED:1.2
+ B_FROM_INITIALS_BUT_ONE:1.1.1.1.0.4
+ B_FROM_INITIALS:1.1.1.1.0.2
+ T_ALL_INITIAL_FILES_BUT_ONE:1.1.1.1
+ T_ALL_INITIAL_FILES:1.1.1.1
+ vendortag:1.1.1.1
+ vendorbranch:1.1.1;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2003.05.23.00.17.53; author jrandom; state Exp;
+branches
+ 1.3.2.1;
+next 1.2;
+
+1.2
+date 2003.05.23.00.15.26; author jrandom; state Exp;
+branches;
+next 1.1;
+
+1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches
+ 1.1.1.1;
+next ;
+
+1.1.1.1
+date 2003.05.22.23.20.19; author jrandom; state Exp;
+branches;
+next ;
+
+1.3.2.1
+date 2003.06.03.04.33.13; author jrandom; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@Second commit to proj, affecting all 7 files.
+@
+text
+@This is sub3/default.
+
+Every directory in the `proj' project has a file named `default'.
+
+This line was added by the first commit (affecting two files).
+
+This line was added in the second commit (affecting all 7 files).
+@
+
+
+1.3.2.1
+log
+@This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT. Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+text
+@a7 4
+
+This change affects sub3/default and sub1/subsubB/default, on branch
+B_SPLIT. Note that the latter file did not even exist on this branch
+until after some other files had had revisions committed on B_SPLIT.
+@
+
+
+1.2
+log
+@First commit to proj, affecting two files.
+@
+text
+@d6 2
+@
+
+
+1.1
+log
+@Initial revision
+@
+text
+@d4 2
+@
+
+
+1.1.1.1
+log
+@Initial import.
+@
+text
+@@
diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh
new file mode 100755
index 0000000000..958bdce4dd
--- /dev/null
+++ b/t/t9603-cvsimport-patchsets.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+# Structure of the test cvs repository
+#
+# Message File:Content Commit Time
+# Rev 1 a: 1.1 2009-02-21 19:11:43 +0100
+# Rev 2 a: 1.2 b: 1.1 2009-02-21 19:11:14 +0100
+# Rev 3 b: 1.2 2009-02-21 19:11:43 +0100
+#
+# As you can see the commit of Rev 3 has the same time as
+# Rev 1 this leads to a broken import because of a cvsps
+# bug.
+
+test_description='git cvsimport testing for correct patchset estimation'
+. ./lib-cvs.sh
+
+CVSROOT="$TEST_DIRECTORY"/t9603/cvsroot
+export CVSROOT
+
+test_expect_failure 'import with criss cross times on revisions' '
+
+ git cvsimport -p"-x" -C module-git module &&
+ cd module-git &&
+ git log --pretty=format:%s > ../actual-master &&
+ git log A~2..A --pretty="format:%s %ad" -- > ../actual-A &&
+ echo "" >> ../actual-master &&
+ echo "" >> ../actual-A &&
+ cd .. &&
+ echo "Rev 4
+Rev 3
+Rev 2
+Rev 1" > expect-master &&
+ test_cmp actual-master expect-master &&
+
+ echo "Rev 5 Branch A Wed Mar 11 19:09:10 2009 +0000
+Rev 4 Branch A Wed Mar 11 19:03:52 2009 +0000" > expect-A &&
+ test_cmp actual-A expect-A
+'
+
+test_done
diff --git a/t/t9603/cvsroot/.gitattributes b/t/t9603/cvsroot/.gitattributes
new file mode 100644
index 0000000000..562b12e16e
--- /dev/null
+++ b/t/t9603/cvsroot/.gitattributes
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9603/cvsroot/CVSROOT/.gitignore b/t/t9603/cvsroot/CVSROOT/.gitignore
new file mode 100644
index 0000000000..3bb9b34173
--- /dev/null
+++ b/t/t9603/cvsroot/CVSROOT/.gitignore
@@ -0,0 +1,2 @@
+history
+val-tags
diff --git a/t/t9603/cvsroot/module/a,v b/t/t9603/cvsroot/module/a,v
new file mode 100644
index 0000000000..ba8fd5af23
--- /dev/null
+++ b/t/t9603/cvsroot/module/a,v
@@ -0,0 +1,74 @@
+head 1.2;
+access;
+symbols
+ A:1.2.0.2;
+locks; strict;
+comment @# @;
+
+
+1.2
+date 2009.02.21.18.11.14; author tester; state Exp;
+branches
+ 1.2.2.1;
+next 1.1;
+
+1.1
+date 2009.02.21.18.11.43; author tester; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2009.03.11.19.03.52; author tester; state Exp;
+branches;
+next 1.2.2.2;
+
+1.2.2.2
+date 2009.03.11.19.09.10; author tester; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.2
+log
+@Rev 2
+@
+text
+@1.2
+@
+
+
+1.2.2.1
+log
+@Rev 4 Branch A
+@
+text
+@d1 1
+a1 1
+1.2.2.1
+@
+
+
+1.2.2.2
+log
+@Rev 5 Branch A
+@
+text
+@d1 1
+a1 1
+1.2.2.2
+@
+
+
+1.1
+log
+@Rev 1
+@
+text
+@d1 1
+a1 1
+1.1
+@
diff --git a/t/t9603/cvsroot/module/b,v b/t/t9603/cvsroot/module/b,v
new file mode 100644
index 0000000000..d26885518a
--- /dev/null
+++ b/t/t9603/cvsroot/module/b,v
@@ -0,0 +1,90 @@
+head 1.3;
+access;
+symbols
+ A:1.2.0.2;
+locks; strict;
+comment @# @;
+
+
+1.3
+date 2009.03.11.19.05.08; author tester; state Exp;
+branches;
+next 1.2;
+
+1.2
+date 2009.02.21.18.11.43; author tester; state Exp;
+branches
+ 1.2.2.1;
+next 1.1;
+
+1.1
+date 2009.02.21.18.11.14; author tester; state Exp;
+branches;
+next ;
+
+1.2.2.1
+date 2009.03.11.19.03.52; author tester; state Exp;
+branches;
+next 1.2.2.2;
+
+1.2.2.2
+date 2009.03.11.19.09.10; author tester; state Exp;
+branches;
+next ;
+
+
+desc
+@@
+
+
+1.3
+log
+@Rev 4
+@
+text
+@1.3
+@
+
+
+1.2
+log
+@Rev 3
+@
+text
+@d1 1
+a1 1
+1.2
+@
+
+
+1.2.2.1
+log
+@Rev 4 Branch A
+@
+text
+@d1 1
+a1 1
+1.2.2.1
+@
+
+
+1.2.2.2
+log
+@Rev 5 Branch A
+@
+text
+@d1 1
+a1 1
+1.2
+@
+
+
+1.1
+log
+@Rev 2
+@
+text
+@d1 1
+a1 1
+1.1
+@
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 5fdc5d94a2..a5b8d03db0 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"
diff --git a/transport.c b/transport.c
index de0d5874a3..faee154c38 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 51b539778c..171a01c7a3 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 1e0e2325f1..d19df44f40 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 841ebb534a..f7d308a411 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);
}
diff --git a/ws.c b/ws.c
index 8d855b7fd5..59d0883c1f 100644
--- a/ws.c
+++ b/ws.c
@@ -262,10 +262,10 @@ int ws_fix_copy(char *dst, const char *src, int len, unsigned ws_rule, int *erro
* Strip trailing whitespace
*/
if (ws_rule & WS_TRAILING_SPACE) {
- if (1 < len && src[len - 1] == '\n') {
+ if (0 < len && src[len - 1] == '\n') {
add_nl_to_tail = 1;
len--;
- if (1 < len && src[len - 1] == '\r') {
+ if (0 < len && src[len - 1] == '\r') {
add_cr_to_tail = !!(ws_rule & WS_CR_AT_EOL);
len--;
}