]> rtime.felk.cvut.cz Git - git.git/commitdiff
Merge branch 'js/run-command-updates'
authorJunio C Hamano <gitster@pobox.com>
Tue, 11 Aug 2009 05:14:57 +0000 (22:14 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 11 Aug 2009 05:14:57 +0000 (22:14 -0700)
* js/run-command-updates:
  api-run-command.txt: describe error behavior of run_command functions
  run-command.c: squelch a "use before assignment" warning
  receive-pack: remove unnecessary run_status report
  run_command: report failure to execute the program, but optionally don't
  run_command: encode deadly signal number in the return value
  run_command: report system call errors instead of returning error codes
  run_command: return exit code as positive value
  MinGW: simplify waitpid() emulation macros

227 files changed:
Documentation/Makefile
Documentation/RelNotes-1.6.3.4.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.4.txt
Documentation/RelNotes-1.6.5.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/diff-format.txt
Documentation/git-diff-files.txt
Documentation/git-diff-index.txt
Documentation/git-diff-tree.txt
Documentation/git-diff.txt
Documentation/git-fast-export.txt
Documentation/git-format-patch.txt
Documentation/git-grep.txt
Documentation/git-init-db.txt
Documentation/git-init.txt
Documentation/git-ls-files.txt
Documentation/git-merge-base.txt
Documentation/git-pack-objects.txt
Documentation/git-prune-packed.txt
Documentation/git-push.txt
Documentation/git-read-tree.txt
Documentation/git-rerere.txt
Documentation/git-rev-list.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-show-branch.txt
Documentation/git-show-ref.txt
Documentation/git-stash.txt
Documentation/git-submodule.txt
Documentation/git-svn.txt
Documentation/git-tag.txt
Documentation/git-verify-pack.txt
Documentation/git.txt
Documentation/pt_BR/gittutorial.txt [new file with mode: 0644]
Documentation/rev-list-options.txt
Documentation/technical/api-tree-walking.txt
GIT-VERSION-GEN
Makefile
RelNotes
abspath.c
bisect.c
branch.c
builtin-add.c
builtin-apply.c
builtin-archive.c
builtin-blame.c
builtin-branch.c
builtin-clean.c
builtin-clone.c
builtin-commit-tree.c
builtin-commit.c
builtin-config.c
builtin-describe.c
builtin-diff.c
builtin-fast-export.c
builtin-fetch--tool.c
builtin-fetch.c
builtin-fmt-merge-msg.c
builtin-fsck.c
builtin-grep.c
builtin-init-db.c
builtin-log.c
builtin-ls-files.c
builtin-mailsplit.c
builtin-merge-base.c
builtin-merge.c
builtin-mv.c
builtin-pack-objects.c
builtin-prune-packed.c
builtin-push.c
builtin-read-tree.c
builtin-reflog.c
builtin-rev-parse.c
builtin-revert.c
builtin-rm.c
builtin-send-pack.c
builtin-show-branch.c
builtin-show-ref.c
builtin-stripspace.c
builtin-tag.c
builtin-tar-tree.c
builtin-unpack-objects.c
builtin-verify-pack.c
builtin-verify-tag.c
builtin-write-tree.c
cache-tree.c
cache.h
combine-diff.c
commit.c
config.c
config.mak.in
configure.ac
contrib/completion/git-completion.bash
contrib/emacs/git.el
contrib/hg-to-git/hg-to-git.py
csum-file.c
daemon.c
diff.c
dir.c
dir.h
entry.c
environment.c
fast-import.c
fsck.c
git-am.sh
git-compat-util.h
git-cvsexportcommit.perl
git-instaweb.sh
git-pull.sh
git-rebase--interactive.sh
git-rebase.sh
git-repack.sh
git-request-pull.sh
git-send-email.perl
git-stash.sh
git-svn.perl
git.c
gitweb/README
gitweb/gitweb.css
gitweb/gitweb.perl
graph.c
grep.c
grep.h
hash-object.c
help.c
http-push.c
http.c
index-pack.c
ll-merge.c
merge-recursive.c
mktag.c
pack-refs.c
pack-revindex.c
pack-write.c
parse-options.c
parse-options.h
pkt-line.c
preload-index.c
read-cache.c
refs.c
remote.c
revision.c
run-command.c
setup.c
sha1_file.c
sha1_name.c
shell.c
symlinks.c
t/Makefile
t/lib-cvs.sh [new file with mode: 0644]
t/t0001-init.sh
t/t1300-repo-config.sh
t/t1502-rev-parse-parseopt.sh
t/t3404-rebase-interactive.sh
t/t3411-rebase-preserve-around-merges.sh
t/t3414-rebase-preserve-onto.sh [new file with mode: 0755]
t/t4014-format-patch.sh
t/t4020-diff-external.sh
t/t4038-diff-combined.sh [new file with mode: 0755]
t/t4124-apply-ws-rule.sh
t/t4132-apply-removal.sh [new file with mode: 0755]
t/t4202-log.sh
t/t5502-quickfetch.sh
t/t5510-fetch.sh
t/t5520-pull.sh
t/t5530-upload-pack-error.sh
t/t6010-merge-base.sh
t/t6020-merge-df.sh
t/t7002-grep.sh
t/t7700-repack.sh
t/t8005-blame-i18n.sh
t/t8005/euc-japan.txt [new file with mode: 0644]
t/t8005/sjis.txt
t/t8005/utf8.txt
t/t9101-git-svn-props.sh
t/t9107-git-svn-migrate.sh
t/t9140-git-svn-reset.sh [moved from t/t9139-git-svn-reset.sh with 100% similarity]
t/t9141-git-svn-multiple-branches.sh [moved from t/t9138-git-svn-multiple-branches.sh with 93% similarity]
t/t9142-git-svn-shallow-clone.sh [new file with mode: 0755]
t/t9143-git-svn-gc.sh [new file with mode: 0755]
t/t9200-git-cvsexportcommit.sh
t/t9301-fast-export.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9600-cvsimport.sh
t/t9601-cvsimport-vendor-branch.sh [new file with mode: 0755]
t/t9601/cvsroot/.gitattributes [new file with mode: 0644]
t/t9601/cvsroot/CVSROOT/.gitignore [new file with mode: 0644]
t/t9601/cvsroot/module/added-imported.txt,v [new file with mode: 0644]
t/t9601/cvsroot/module/imported-anonymously.txt,v [new file with mode: 0644]
t/t9601/cvsroot/module/imported-modified-imported.txt,v [new file with mode: 0644]
t/t9601/cvsroot/module/imported-modified.txt,v [new file with mode: 0644]
t/t9601/cvsroot/module/imported-once.txt,v [new file with mode: 0644]
t/t9601/cvsroot/module/imported-twice.txt,v [new file with mode: 0644]
t/t9602-cvsimport-branches-tags.sh [new file with mode: 0755]
t/t9602/README [new file with mode: 0644]
t/t9602/cvsroot/.gitattributes [new file with mode: 0644]
t/t9602/cvsroot/CVSROOT/.gitignore [new file with mode: 0644]
t/t9602/cvsroot/module/default,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub1/default,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub1/subsubA/default,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub1/subsubB/default,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub2/Attic/branch_B_MIXED_only,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub2/default,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub2/subsubA/default,v [new file with mode: 0644]
t/t9602/cvsroot/module/sub3/default,v [new file with mode: 0644]
t/t9603-cvsimport-patchsets.sh [new file with mode: 0755]
t/t9603/cvsroot/.gitattributes [new file with mode: 0644]
t/t9603/cvsroot/CVSROOT/.gitignore [new file with mode: 0644]
t/t9603/cvsroot/module/a,v [new file with mode: 0644]
t/t9603/cvsroot/module/b,v [new file with mode: 0644]
t/test-lib.sh
test-sha1.c
transport.c
transport.h
unpack-file.c
unpack-trees.c
unpack-trees.h
upload-pack.c
usage.c
walker.c
wrapper.c
write_or_die.c
ws.c
wt-status.c
xdiff-interface.c
xdiff-interface.h
xdiff/xdiffi.c

index 7a8037f586773c00004e34079b48bb514db2515f..06b0c57b95057b3df4f215f3191fd8e9fc473e6f 100644 (file)
@@ -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 (file)
index 0000000..cad461b
--- /dev/null
@@ -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.
+
index af68297af537b2e4cbd4d4bcbe94cb382486fa05..7a904419f7815094b68c864a558624667d5a91d1 100644 (file)
@@ -22,13 +22,6 @@ branch pointed at by its HEAD, gets a large warning.  You can choose what
 should happen upon such a push by setting the configuration variable
 receive.denyDeleteCurrent in the receiving repository.
 
-When the user does not tell "git push" what to push, it has always
-pushed matching refs.  For some people it is unexpected, and a new
-configuration variable push.default has been introduced to allow
-changing a different default behaviour.  To advertise the new feature,
-a big warning is issued if this is not configured and a git push without
-arguments is attempted.
-
 
 Updates since v1.6.3
 --------------------
@@ -38,26 +31,67 @@ Updates since v1.6.3
  * gitweb Perl style clean-up.
 
  * git-svn updates, including a new --authors-prog option to map author
-   names by invoking an external program.
+   names by invoking an external program, 'git svn reset' to unwind
+   '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.
+   understood more widely.  Similarly updated test scripts to use
+   encoding names more widely understood (e.g. use "ISO8859-1" instead
+   of "ISO-8859-1").
+
+ * Various portability fixes/workarounds for different vintages of
+   SunOS, IRIX, and Windows.
+
+ * Git-over-ssh transport on Windows supports PuTTY plink and TortoisePlink.
 
 (performance)
 
+ * Many repeated use of lstat() are optimized out in "checkout" codepath.
+
+ * git-status (and underlying git-diff-index --cached) are optimized
+   to take advantage of cache-tree information in the index.
+
 (usability, bells and whistles)
 
  * "git add --edit" lets users edit the whole patch text to fine-tune what
    is added to the index.
 
- * "git log --graph" draws graphs more compactly by using horizonal lines
+ * "git am" accepts StGIT series file as its input.
+
+ * "git bisect skip" skips to a more randomly chosen place in the hope
+   to avoid testing a commit that is too close to a commit that is
+   already known to be untestable.
+
+ * "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.
+
+ * https transport can optionally be told that the used client
+   certificate is password protected, in which case it asks the
+   password only once.
+
+ * "git imap-send" is IPv6 aware.
+
+ * "git log --graph" draws graphs more compactly by using horizontal lines
    when able.
 
  * "git log --decorate" shows shorter refnames by stripping well-known
    refs/* prefix.
 
+ * "git push $name" honors remote.$name.pushurl if present before
+   using remote.$name.url.  In other words, the URL used for fetching
+   and pushing can be different.
+
  * "git send-email" understands quoted aliases in .mailrc files (might
    have to be backported to 1.6.3.X).
 
@@ -69,10 +103,17 @@ Updates since v1.6.3
  * "add" and "update" subcommands to "git submodule" learned --reference
    option to use local clone with references.
 
+ * "git submodule update" learned --rebase option to update checked
+   out submodules by rebasing the local changes.
+
+ * "gitweb" can optionally use gravatar to adorn author/committer names.
+
 (developers)
 
  * 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
 ------------------
 
@@ -82,12 +123,25 @@ release, unless otherwise noted.
 Here are fixes that this release has, but have not been backported to
 v1.6.3.X series.
 
+ * "git diff-tree -r -t" used to omit new or removed directories from
+   the output.  df533f3 (diff-tree -r -t: include added/removed
+   directories in the output, 2009-06-13) may need to be cherry-picked
+   to backport this fix.
+
  * The way Git.pm sets up a Repository object was not friendly to callers
    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.3.1-168-g23807fa
-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 (file)
index 0000000..856047d
--- /dev/null
@@ -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.
+
+
index 2fecbe32a68bd981959735dd14e6e311d16b233a..c6f09f801a8dbb2f03e73ab5dd11c1139332fa5f 100644 (file)
@@ -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
@@ -456,7 +457,9 @@ If the alias expansion is prefixed with an exclamation point,
 it will be treated as a shell command.  For example, defining
 "alias.new = !gitk --all --not ORIG_HEAD", the invocation
 "git new" is equivalent to running the shell command
-"gitk --all --not ORIG_HEAD".
+"gitk --all --not ORIG_HEAD".  Note that shell commands will be
+executed from the top-level directory of a repository, which may
+not necessarily be the current directory.
 
 apply.whitespace::
        Tells 'git-apply' how to handle whitespaces, in the same way
@@ -1043,6 +1046,12 @@ http.sslKey::
        over HTTPS. Can be overridden by the 'GIT_SSL_KEY' environment
        variable.
 
+http.sslCertPasswordProtected::
+       Enable git's password prompt for the SSL certificate.  Otherwise
+       OpenSSL will prompt the user, possibly many times, if the
+       certificate or private key is encrypted.  Can be overridden by the
+       'GIT_SSL_CERT_PASSWORD_PROTECTED' environment variable.
+
 http.sslCAInfo::
        File containing the certificates to verify the peer with when
        fetching or pushing over HTTPS. Can be overridden by the
@@ -1379,6 +1388,50 @@ rerere.enabled::
        default enabled if you create `rr-cache` directory under
        `$GIT_DIR`, but can be disabled by setting this option to false.
 
+sendemail.identity::
+       A configuration identity. When given, causes values in the
+       'sendemail.<identity>' subsection to take precedence over
+       values in the 'sendemail' section. The default identity is
+       the value of 'sendemail.identity'.
+
+sendemail.smtpencryption::
+       See linkgit:git-send-email[1] for description.  Note that this
+       setting is not subject to the 'identity' mechanism.
+
+sendemail.smtpssl::
+       Deprecated alias for 'sendemail.smtpencryption = ssl'.
+
+sendemail.<identity>.*::
+       Identity-specific versions of the 'sendemail.*' parameters
+       found below, taking precedence over those when the this
+       identity is selected, through command-line or
+       'sendemail.identity'.
+
+sendemail.aliasesfile::
+sendemail.aliasfiletype::
+sendemail.bcc::
+sendemail.cc::
+sendemail.cccmd::
+sendemail.chainreplyto::
+sendemail.confirm::
+sendemail.envelopesender::
+sendemail.from::
+sendemail.multiedit::
+sendemail.signedoffbycc::
+sendemail.smtppass::
+sendemail.suppresscc::
+sendemail.suppressfrom::
+sendemail.to::
+sendemail.smtpserver::
+sendemail.smtpserverport::
+sendemail.smtpuser::
+sendemail.thread::
+sendemail.validate::
+       See linkgit:git-send-email[1] for description.
+
+sendemail.signedoffcc::
+       Deprecated alias for 'sendemail.signedoffbycc'.
+
 showbranch.default::
        The default set of branches for linkgit:git-show-branch[1].
        See linkgit:git-show-branch[1].
index 1eeb1c76838c1911fc4d57b36a16dece0538809a..b71712473ed13020bca3f133ea4a28c5081b7f9e 100644 (file)
@@ -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:
 
index c5261415643d359648900e17f522ba7b96fed44a..4ef03578ebc58f3b678e8805644ae8c0ad4b998b 100644 (file)
@@ -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[]
 
 
index 26920d4f63cd213ff17ab28d8dd0dbea94482147..8b9ed2929980ec9928930496375df5e9d72e4b51 100644 (file)
@@ -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
index 23b7abd3c6b0e02eb325983eaad66598c42fc8be..f2cef1260b7f74254b96ce65f8aa55267b618c29 100644 (file)
@@ -159,8 +159,7 @@ HEAD commits it finds, which is even more interesting.
 
 in case you care).
 
-Output format
--------------
+
 include::diff-format.txt[]
 
 
index a2f192fb7519117f8e47f3ec4608e3301c200dc3..0ac711230e24bf7238b2e3a215cdc3c4ce0d2087 100644 (file)
@@ -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
index 0c9eb567cb3e3af89a2e2613d1e074941fa30d13..75b06f33e73497fbb8916ef02d70ca59f4aab2f6 100644 (file)
@@ -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
 --------
index 6f1fc80119600c419d2e3b98bac0c44f9cb09026..687e66759817bc2f349e8a5a89aa2c6731237b23 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git format-patch' [-k] [(-o|--output-directory) <dir> | --stdout]
-                  [--thread[=<style>]]
+                  [--no-thread | --thread[=<style>]]
                   [(--attach|--inline)[=<boundary>] | --no-attach]
                   [-s | --signoff]
                   [-n | --numbered | -N | --no-numbered]
@@ -124,17 +124,25 @@ include::diff-options.txt[]
        second part, with "Content-Disposition: inline".
 
 --thread[=<style>]::
-       Add In-Reply-To and References headers to make the second and
-       subsequent mails appear as replies to the first.  Also generates
-       the Message-Id header to reference.
+--no-thread::
+       Controls addition of In-Reply-To and References headers to
+       make the second and subsequent mails appear as replies to the
+       first.  Also controls generation of the Message-Id header to
+       reference.
 +
 The optional <style> argument can be either `shallow` or `deep`.
 'shallow' threading makes every mail a reply to the head of the
 series, where the head is chosen from the cover letter, the
 `\--in-reply-to`, and the first patch mail, in this order.  'deep'
-threading makes every mail a reply to the previous one.  If not
-specified, defaults to the 'format.thread' configuration, or `shallow`
-if that is not set.
+threading makes every mail a reply to the previous one.
++
+The default is --no-thread, unless the 'format.thread' configuration
+is set.  If --thread is specified without a style, it defaults to the
+style specified by 'format.thread' if any, or else `shallow`.
++
+Beware that the default for 'git send-email' is to thread emails
+itself.  If you want 'git format-patch' to take care of hreading, you
+will want to ensure that threading is disabled for 'git send-email'.
 
 --in-reply-to=Message-Id::
        Make the first mail (or all the mails with --no-thread) appear as a
index fccb82deb48ec75d25f948e7caa69a44e09f5832..b753c9d76fde51198eb7ef34b9ff298095fb1f72 100644 (file)
@@ -122,6 +122,14 @@ OPTIONS
 -<num>::
        A shortcut for specifying -C<num>.
 
+-p::
+--show-function::
+       Show the preceding line that contains the function name of
+       the match, unless the matching line is a function name itself.
+       The name is determined in the same way as 'git diff' works out
+       patch hunk headers (see 'Defining a custom hunk-header' in
+       linkgit:gitattributes[5]).
+
 -f <file>::
        Read patterns from <file>, one per line.
 
index 1fd0ff2610a1375bcf0defe2a234b2dee1a7997a..eba3cb4998b2732a22551aaf962ae1a742d221de 100644 (file)
@@ -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
index 7151d12f349b7c6e265d5a4631029d71028a2c7d..f081b24d9d8233677388d437b8f7e6d9402b261b 100644 (file)
@@ -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).
+
 --
 
 
index 057a021eb50899ed5fe1ee5a7b6c59e7fc27becf..021066e95d88624d2ec6c90a9218a4fd70ec17b7 100644 (file)
@@ -44,7 +44,7 @@ OPTIONS
 
 -o::
 --others::
-       Show other files in the output
+       Show other (i.e. untracked) files in the output
 
 -i::
 --ignored::
index 767486c770afd385d118ee5f9a6f9cd3ad0a2d73..ce5b369985c254ec5d986aa3dd250828cf3cc4cb 100644 (file)
@@ -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.
 
index 7d4c1a75562de80aa80dfbfa665330b14ec948c6..2e4992970e84a1789cb97d35969976d12e5a30e1 100644 (file)
@@ -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
 ------
index b5f26cee132622185457d92522fb932302dec97d..abfc6b6ead534311d8a29696c497ecf94ce4fd1a 100644 (file)
@@ -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
index fd53c49fb886b196ba79e40c4c9c4efe0dae5432..2653388fd8f5fac9292ece83a116accdf0978902 100644 (file)
@@ -85,6 +85,11 @@ nor in any Push line of the corresponding remotes file---see below).
 --dry-run::
        Do everything except actually send the updates.
 
+--porcelain::
+       Produce machine-readable output.  The output status line for each ref
+       will be tab-separated and sent to stdout instead of stderr.  The full
+       symbolic names of the refs will be given.
+
 --tags::
        All refs under `$GIT_DIR/refs/tags` are pushed, in
        addition to refspecs explicitly listed on the command
@@ -148,6 +153,12 @@ representing the status of a single ref. Each line is of the form:
  <flag> <summary> <from> -> <to> (<reason>)
 -------------------------------
 
+If --porcelain is used, then each line of the output is of the form:
+
+-------------------------------
+ <flag> \t <from>:<to> \t <summary> (<reason>)
+-------------------------------
+
 flag::
        A single character indicating the status of the ref. This is
        blank for a successfully pushed ref, `!` for a ref that was
index 7160fa1536e3af2edbdfb6d1049f2145bf2b4f50..4a932b08c6ed4f1de46d51fd92046d5190f831a7 100644 (file)
@@ -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
index a53c3cd35b8d6c8be27d90f64013b377d205f201..7dd515b8ccde701ccf5743f80b3b762813ea90c9 100644 (file)
@@ -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.
 
 
index 1c9cc28895a6ea3fcfd978f940e3fa327219de0a..bf98c8449c2cf2d7adef849f0f4fda08427c0487 100644 (file)
@@ -14,6 +14,7 @@ SYNOPSIS
             [ \--max-age=timestamp ]
             [ \--min-age=timestamp ]
             [ \--sparse ]
+            [ \--merges ]
             [ \--no-merges ]
             [ \--first-parent ]
             [ \--remove-empty ]
@@ -50,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
index 4bbdd056da4ca5a3032b53afac820b86c74a54e4..82045a2522799ccf3a03479bf4f4cd1fa1809879 100644 (file)
@@ -30,6 +30,11 @@ OPTIONS
        Only meaningful in `--parseopt` mode. Tells the option parser to echo
        out the first `--` met instead of skipping it.
 
+--stop-at-non-option::
+       Only meaningful in `--parseopt` mode.  Lets the option parser stop at
+       the first non-option argument.  This can be used to parse sub-commands
+       that take options themself.
+
 --sq-quote::
        Use 'git-rev-parse' in shell quoting mode (see SQ-QUOTE
        section below). In contrast to the `--sq` option below, this
index fbde2d3be523443c8388f9bb43cc0d5682658f8f..767cf4d4bdcdfbb6e76b3680b0e89af2186d632a 100644 (file)
@@ -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.
 
@@ -212,11 +213,22 @@ specified, as well as 'body' if --no-signed-off-cc is specified.
        value; if that is unspecified, default to --no-suppress-from.
 
 --[no-]thread::
-       If this is set, the In-Reply-To header will be set on each email sent.
-       If disabled with "--no-thread", no emails will have the In-Reply-To
-       header set, unless specified with --in-reply-to.
-       Default is the value of the 'sendemail.thread' configuration
-       value; if that is unspecified, default to --thread.
+       If this is set, the In-Reply-To and References headers will be
+       added to each email sent.  Whether each mail refers to the
+       previous email (`deep` threading per 'git format-patch'
+       wording) or to the first email (`shallow` threading) is
+       governed by "--[no-]chain-reply-to".
++
+If disabled with "--no-thread", those headers will not be added
+(unless specified with --in-reply-to).  Default is the value of the
+'sendemail.thread' configuration value; if that is unspecified,
+default to --thread.
++
+It is up to the user to ensure that no In-Reply-To header already
+exists when 'git send-email' is asked to add it (especially note that
+'git format-patch' can be configured to do the threading itself).
+Failure to do so may not produce the expected result in the
+recipient's MUA.
 
 
 Administering
index 89ec5364ecd447f6a7836d9c7f582dbd1d864807..734336119c6b1f7ea8241f0404eaa3ba2ae10f69 100644 (file)
@@ -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
index 98e294aa869d39575ab32d859ca9fcc3bfcaf789..f4429bdc6863ce1d115b47282f2c04a4d9093d29 100644 (file)
@@ -9,8 +9,9 @@ SYNOPSIS
 --------
 [verse]
 'git show-ref' [-q|--quiet] [--verify] [-h|--head] [-d|--dereference]
-            [-s|--hash] [--abbrev] [--tags] [--heads] [--] <pattern>...
-'git show-ref' --exclude-existing[=pattern]
+            [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags]
+            [--heads] [--] <pattern>...
+'git show-ref' --exclude-existing[=<pattern>] < ref-list
 
 DESCRIPTION
 -----------
@@ -48,7 +49,7 @@ OPTIONS
        appended.
 
 -s::
---hash::
+--hash[=<n>]::
 
        Only show the SHA1 hash, not the reference name. When combined with
        --dereference the dereferenced tag will still be shown after the SHA1.
@@ -59,11 +60,10 @@ OPTIONS
        Aside from returning an error code of 1, it will also print an error
        message if '--quiet' was not specified.
 
---abbrev::
---abbrev=len::
+--abbrev[=<n>]::
 
        Abbreviate the object name.  When using `--hash`, you do
-       not have to say `--hash --abbrev`; `--hash=len` would do.
+       not have to say `--hash --abbrev`; `--hash=n` would do.
 
 -q::
 --quiet::
@@ -71,8 +71,7 @@ OPTIONS
        Do not print any results to stdout. When combined with '--verify' this
        can be used to silently check if a reference exists.
 
---exclude-existing::
---exclude-existing=pattern::
+--exclude-existing[=<pattern>]::
 
        Make 'git-show-ref' act as a filter that reads refs from stdin of the
        form "^(?:<anything>\s)?<refname>(?:\^\{\})?$" and performs the
index 1c64a02fe576a6bdad2afcbc96391ce0c2d541c4..2f5ca7b1a361ee3b9147c4ba27535f9bb5a2fccb 100644 (file)
@@ -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],
index 683ba1a1ebe080e6e093d27686068823448e459c..7dd73ae14eb38fd5bd291c2251f64768d198ebe5 100644 (file)
@@ -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>...]
 
index 7e9b9a042347bf286c6ea523bef1952f0c72dec9..22a0389f1ed12a79f0b5784c30e0b2be9716c170 100644 (file)
@@ -11,11 +11,11 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-'git-svn' is a simple conduit for changesets between Subversion and git.
+'git svn' is a simple conduit for changesets between Subversion and git.
 It provides a bidirectional flow of changes between a Subversion and a git
 repository.
 
-'git-svn' can track a standard Subversion repository,
+'git svn' can track a standard Subversion repository,
 following the common "trunk/branches/tags" layout, with the --stdlayout option.
 It can also follow branches and tags in any layout with the -T/-t/-b options
 (see options to 'init' below, and also the 'clone' command).
@@ -26,11 +26,10 @@ Subversion updated from git by the 'dcommit' command.
 
 COMMANDS
 --------
---
 
 'init'::
        Initializes an empty git repository with additional
-       metadata directories for 'git-svn'.  The Subversion URL
+       metadata directories for 'git svn'.  The Subversion URL
        may be specified as a command-line argument, or as full
        URL arguments to -T/-t/-b.  Optionally, the target
        directory to operate on can be specified as a second
@@ -63,16 +62,6 @@ COMMANDS
        Set the 'useSvnsyncProps' option in the [svn-remote] config.
 --rewrite-root=<URL>;;
        Set the 'rewriteRoot' option in the [svn-remote] config.
---use-log-author;;
-       When retrieving svn commits into git (as part of fetch, rebase, or
-       dcommit operations), look for the first From: or Signed-off-by: line
-       in the log message and use that as the author string.
---add-author-from;;
-       When committing to svn from git (as part of commit or dcommit
-       operations), if the existing log message doesn't already have a
-       From: or Signed-off-by: line, append a From: line based on the
-       git commit's author string.  If you use this, then --use-log-author
-       will retrieve a valid author string for all commits.
 --username=<USER>;;
        For transports that SVN handles authentication for (http,
        https, and plain svn), specify the username.  For other
@@ -91,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
@@ -100,12 +100,12 @@ COMMANDS
 
 --localtime;;
        Store Git commit times in the local timezone instead of UTC.  This
-       makes 'git-log' (even without --date=local) show the same times
+       makes 'git log' (even without --date=local) show the same times
        that `svn log` would in the local timezone.
 
 --parent;;
        Fetch only from the SVN parent of the current HEAD.
-
++
 This doesn't interfere with interoperating with the Subversion
 repository you cloned from, but if you wish for your local Git
 repository to be able to interoperate with someone else's local Git
@@ -118,20 +118,39 @@ the same local timezone.
        The '--ignore-paths' option should match for every 'fetch'
        (including automatic fetches due to 'clone', 'dcommit',
        'rebase', etc) on a given repository.
-
++
+[verse]
 config key: svn-remote.<name>.ignore-paths
-
-       If the ignore-paths config key is set and the command
-       line option is also given, both regular expressions
-       will be used.
-
++
+If the ignore-paths config key is set and the command line option is
+also given, both regular expressions will be used.
++
 Examples:
++
+--
+Skip "doc*" directory for every fetch;;
++
+------------------------------------------------------------------------
+--ignore-paths="^doc"
+------------------------------------------------------------------------
 
-       --ignore-paths="^doc" - skip "doc*" directory for every
-           fetch.
+Skip "branches" and "tags" of first level directories;;
++
+------------------------------------------------------------------------
+--ignore-paths="^[^/]+/(?:branches|tags)"
+------------------------------------------------------------------------
+--
 
-       --ignore-paths="^[^/]+/(?:branches|tags)" - skip
-           "branches" and "tags" of first level directories.
+--use-log-author;;
+       When retrieving svn commits into git (as part of fetch, rebase, or
+       dcommit operations), look for the first From: or Signed-off-by: line
+       in the log message and use that as the author string.
+--add-author-from;;
+       When committing to svn from git (as part of commit or dcommit
+       operations), if the existing log message doesn't already have a
+       From: or Signed-off-by: line, append a From: line based on the
+       git commit's author string.  If you use this, then --use-log-author
+       will retrieve a valid author string for all commits.
 
 'clone'::
        Runs 'init' and 'fetch'.  It will automatically create a
@@ -139,29 +158,29 @@ Examples:
        or if a second argument is passed; it will create a directory
        and work within that.  It accepts all arguments that the
        'init' and 'fetch' commands accept; with the exception of
-       '--fetch-all'.   After a repository is cloned, the 'fetch'
-       command will be able to update revisions without affecting
-       the working tree; and the 'rebase' command will be able
-       to update the working tree with the latest changes.
+       '--fetch-all' and '--parent'.  After a repository is cloned,
+       the 'fetch' command will be able to update revisions without
+       affecting the working tree; and the 'rebase' command will be
+       able to update the working tree with the latest changes.
 
 'rebase'::
        This fetches revisions from the SVN parent of the current HEAD
        and rebases the current (uncommitted to SVN) work against it.
-
-This works similarly to `svn update` or 'git-pull' except that
-it preserves linear history with 'git-rebase' instead of
-'git-merge' for ease of dcommitting with 'git-svn'.
-
-This accepts all options that 'git-svn fetch' and 'git-rebase'
++
+This works similarly to `svn update` or 'git pull' except that
+it preserves linear history with 'git rebase' instead of
+'git merge' for ease of dcommitting with 'git svn'.
++
+This accepts all options that 'git svn fetch' and 'git rebase'
 accept.  However, '--fetch-all' only fetches from the current
 [svn-remote], and not all [svn-remote] definitions.
-
-Like 'git-rebase'; this requires that the working tree be clean
++
+Like 'git rebase'; this requires that the working tree be clean
 and have no uncommitted changes.
 
 -l;;
 --local;;
-       Do not fetch remotely; only run 'git-rebase' against the
+       Do not fetch remotely; only run 'git rebase' against the
        last fetched commit from the upstream SVN.
 
 'dcommit'::
@@ -169,11 +188,11 @@ and have no uncommitted changes.
        repository, and then rebase or reset (depending on whether or
        not there is a diff between SVN and head).  This will create
        a revision in SVN for each commit in git.
-       It is recommended that you run 'git-svn' fetch and rebase (not
+       It is recommended that you run 'git svn' fetch and rebase (not
        pull or merge) your commits against the latest changes in the
        SVN repository.
        An optional revision or branch argument may be specified, and
-       causes 'git-svn' to do all work on that revision/branch
+       causes 'git svn' to do all work on that revision/branch
        instead of HEAD.
        This is advantageous over 'set-tree' (below) because it produces
        cleaner, more linear history.
@@ -182,18 +201,17 @@ and have no uncommitted changes.
        After committing, do not rebase or reset.
 --commit-url <URL>;;
        Commit to this SVN URL (the full path).  This is intended to
-       allow existing git-svn repositories created with one transport
+       allow existing 'git svn' repositories created with one transport
        method (e.g. `svn://` or `http://` for anonymous read) to be
        reused if a user is later given access to an alternate transport
        method (e.g. `svn+ssh://` or `https://`) for commit.
-
++
+[verse]
 config key: svn-remote.<name>.commiturl
-
 config key: svn.commiturl (overwrites all svn-remote.<name>.commiturl options)
-
-       Using this option for any other purpose (don't ask)
-       is very strongly discouraged.
---
++
+Using this option for any other purpose (don't ask) is very strongly
+discouraged.
 
 'branch'::
        Create a branch in the SVN repository.
@@ -232,10 +250,12 @@ where <name> is the name of the SVN repository as specified by the -R option to
 The following features from `svn log' are supported:
 +
 --
--r/--revision=<n>[:<n>];;
+-r <n>[:<n>];;
+--revision=<n>[:<n>];;
        is supported, non-numeric args are not:
        HEAD, NEXT, BASE, PREV, etc ...
--v/--verbose;;
+-v;;
+--verbose;;
        it's not completely compatible with the --verbose
        output in svn log, but reasonably close.
 --limit=<n>;;
@@ -258,7 +278,7 @@ NOTE: SVN itself only stores times in UTC and nothing else. The regular svn
 client converts the UTC time to the local time (or based on the TZ=
 environment). This command has the same behaviour.
 +
-Any other arguments are passed directly to 'git-log'
+Any other arguments are passed directly to 'git log'
 
 'blame'::
        Show what revision and author last modified each line of a file. The
@@ -266,15 +286,14 @@ Any other arguments are passed directly to 'git-log'
        `svn blame' by default. Like the SVN blame command,
        local uncommitted changes in the working copy are ignored;
        the version of the file in the HEAD revision is annotated. Unknown
-       arguments are passed directly to 'git-blame'.
+       arguments are passed directly to 'git blame'.
 +
 --git-format;;
-       Produce output in the same format as 'git-blame', but with
+       Produce output in the same format as 'git blame', but with
        SVN revision numbers instead of git commit hashes. In this mode,
        changes that haven't been committed to SVN (including local
        working-copy edits) are shown as revision 0.
 
---
 'find-rev'::
        When given an SVN revision number of the form 'rN', returns the
        corresponding git commit hash (this can optionally be followed by a
@@ -288,7 +307,7 @@ Any other arguments are passed directly to 'git-log'
        absolutely no attempts to do patching when committing to SVN, it
        simply overwrites files with those specified in the tree or
        commit.  All merging is assumed to have taken place
-       independently of 'git-svn' functions.
+       independently of 'git svn' functions.
 
 'create-ignore'::
        Recursively finds the svn:ignore property on directories and
@@ -303,12 +322,12 @@ Any other arguments are passed directly to 'git-log'
 
 'commit-diff'::
        Commits the diff of two tree-ish arguments from the
-       command-line.  This command does not rely on being inside an `git-svn
+       command-line.  This command does not rely on being inside an `git svn
        init`-ed repository.  This command takes three arguments, (a) the
        original tree to diff against, (b) the new tree result, (c) the
        URL of the target Subversion repository.  The final argument
-       (URL) may be omitted if you are working from a 'git-svn'-aware
-       repository (that has been `init`-ed with 'git-svn').
+       (URL) may be omitted if you are working from a 'git svn'-aware
+       repository (that has been `init`-ed with 'git svn').
        The -r<revision> option is required for this.
 
 'info'::
@@ -330,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
@@ -340,163 +363,156 @@ Any other arguments are passed directly to 'git-log'
        "checksum mismatch" (missed a modification).  If the problem
        file cannot be ignored forever (with --ignore-paths) the only
        way to repair the repo is to use 'reset'.
-
++
 Only the rev_map and refs/remotes/git-svn are changed.  Follow 'reset'
-with a 'fetch' and then 'git-reset' or 'git-rebase' to move local
+with a 'fetch' and then 'git reset' or 'git rebase' to move local
 branches onto the new tree.
 
--r/--revision=<n>;;
+-r <n>;;
+--revision=<n>;;
        Specify the most recent revision to keep.  All later revisions
        are discarded.
--p/--parent;;
+-p;;
+--parent;;
        Discard the specified revision as well, keeping the nearest
        parent instead.
 Example:;;
 Assume you have local changes in "master", but you need to refetch "r2".
-
++
 ------------
     r1---r2---r3 remotes/git-svn
                 \
                  A---B master
 ------------
-
++
 Fix the ignore-paths or SVN permissions problem that caused "r2" to
 be incomplete in the first place.  Then:
-
++
 [verse]
 git svn reset -r2 -p
 git svn fetch
-
++
 ------------
     r1---r2'--r3' remotes/git-svn
       \
        r2---r3---A---B master
 ------------
-
-Then fixup "master" with 'git-rebase'.
-Do NOT use 'git-merge' or your history will not be compatible with a
++
+Then fixup "master" with 'git rebase'.
+Do NOT use 'git merge' or your history will not be compatible with a
 future 'dcommit'!
-
++
 [verse]
 git rebase --onto remotes/git-svn A^ master
-
++
 ------------
     r1---r2'--r3' remotes/git-svn
                 \
                  A'--B' master
 ------------
 
-
---
-
 OPTIONS
 -------
---
 
 --shared[={false|true|umask|group|all|world|everybody}]::
 --template=<template_directory>::
        Only used with the 'init' command.
-       These are passed directly to 'git-init'.
+       These are passed directly to 'git init'.
 
 -r <ARG>::
 --revision <ARG>::
-
-Used with the 'fetch' command.
-
+          Used with the 'fetch' command.
++
 This allows revision ranges for partial/cauterized history
 to be supported.  $NUMBER, $NUMBER1:$NUMBER2 (numeric ranges),
 $NUMBER:HEAD, and BASE:$NUMBER are all supported.
-
++
 This can allow you to make partial mirrors when running fetch;
 but is generally not recommended because history will be skipped
 and lost.
 
 -::
 --stdin::
-
-Only used with the 'set-tree' command.
-
+       Only used with the 'set-tree' command.
++
 Read a list of commits from stdin and commit them in reverse
 order.  Only the leading sha1 is read from each line, so
-'git-rev-list --pretty=oneline' output can be used.
+'git rev-list --pretty=oneline' output can be used.
 
 --rmdir::
-
-Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
-
+       Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
++
 Remove directories from the SVN tree if there are no files left
 behind.  SVN can version empty directories, and they are not
 removed by default if there are no files left in them.  git
 cannot version empty directories.  Enabling this flag will make
 the commit to SVN act like git.
-
++
+[verse]
 config key: svn.rmdir
 
 -e::
 --edit::
-
-Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
-
+       Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
++
 Edit the commit message before committing to SVN.  This is off by
 default for objects that are commits, and forced on when committing
 tree objects.
-
++
+[verse]
 config key: svn.edit
 
 -l<num>::
 --find-copies-harder::
-
-Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
-
-They are both passed directly to 'git-diff-tree'; see
+       Only used with the 'dcommit', 'set-tree' and 'commit-diff' commands.
++
+They are both passed directly to 'git diff-tree'; see
 linkgit:git-diff-tree[1] for more information.
-
++
 [verse]
 config key: svn.l
 config key: svn.findcopiesharder
 
 -A<filename>::
 --authors-file=<filename>::
-
-Syntax is compatible with the file used by 'git-cvsimport':
-
+       Syntax is compatible with the file used by 'git cvsimport':
++
 ------------------------------------------------------------------------
        loginname = Joe User <user@example.com>
 ------------------------------------------------------------------------
-
-If this option is specified and 'git-svn' encounters an SVN
-committer name that does not exist in the authors-file, 'git-svn'
++
+If this option is specified and 'git svn' encounters an SVN
+committer name that does not exist in the authors-file, 'git svn'
 will abort operation. The user will then have to add the
-appropriate entry.  Re-running the previous 'git-svn' command
+appropriate entry.  Re-running the previous 'git svn' command
 after the authors-file is modified should continue operation.
-
++
+[verse]
 config key: svn.authorsfile
 
 --authors-prog=<filename>::
-
-If this option is specified, for each SVN committer name that does not
-exist in the authors file, the given file is executed with the committer
-name as the first argument.  The program is expected to return a single
-line of the form "Name <email>", which will be treated as if included in
-the authors file.
+       If this option is specified, for each SVN committer name that
+       does not exist in the authors file, the given file is executed
+       with the committer name as the first argument.  The program is
+       expected to return a single line of the form "Name <email>",
+       which will be treated as if included in the authors file.
 
 -q::
 --quiet::
-       Make 'git-svn' less verbose. Specify a second time to make it
+       Make 'git svn' less verbose. Specify a second time to make it
        even less verbose.
 
 --repack[=<n>]::
 --repack-flags=<flags>::
-
-These should help keep disk usage sane for large fetches
-with many revisions.
-
+       These should help keep disk usage sane for large fetches with
+       many revisions.
++
 --repack takes an optional argument for the number of revisions
 to fetch before repacking.  This defaults to repacking every
 1000 commits fetched if no argument is specified.
-
---repack-flags are passed directly to 'git-repack'.
-
++
+--repack-flags are passed directly to 'git repack'.
++
 [verse]
 config key: svn.repack
 config key: svn.repackflags
@@ -505,41 +521,36 @@ config key: svn.repackflags
 --merge::
 -s<strategy>::
 --strategy=<strategy>::
-
-These are only used with the 'dcommit' and 'rebase' commands.
-
-Passed directly to 'git-rebase' when using 'dcommit' if a
-'git-reset' cannot be used (see 'dcommit').
+       These are only used with the 'dcommit' and 'rebase' commands.
++
+Passed directly to 'git rebase' when using 'dcommit' if a
+'git reset' cannot be used (see 'dcommit').
 
 -n::
 --dry-run::
-
-This can be used with the 'dcommit', 'rebase', 'branch' and 'tag'
-commands.
-
+       This can be used with the 'dcommit', 'rebase', 'branch' and
+       'tag' commands.
++
 For 'dcommit', print out the series of git arguments that would show
 which diffs would be committed to SVN.
-
++
 For 'rebase', display the local branch associated with the upstream svn
 repository associated with the current branch and the URL of svn
 repository that will be fetched from.
-
++
 For 'branch' and 'tag', display the urls that will be used for copying when
 creating the branch or tag.
 
---
 
 ADVANCED OPTIONS
 ----------------
---
 
 -i<GIT_SVN_ID>::
 --id <GIT_SVN_ID>::
-
-This sets GIT_SVN_ID (instead of using the environment).  This
-allows the user to override the default refname to fetch from
-when tracking a single URL.  The 'log' and 'dcommit' commands
-no longer require this switch as an argument.
+       This sets GIT_SVN_ID (instead of using the environment).  This
+       allows the user to override the default refname to fetch from
+       when tracking a single URL.  The 'log' and 'dcommit' commands
+       no longer require this switch as an argument.
 
 -R<remote name>::
 --svn-remote <remote name>::
@@ -553,33 +564,30 @@ no longer require this switch as an argument.
        started tracking a branch and never tracked the trunk it was
        descended from. This feature is enabled by default, use
        --no-follow-parent to disable it.
-
++
+[verse]
 config key: svn.followparent
 
---
 CONFIG FILE-ONLY OPTIONS
 ------------------------
---
 
 svn.noMetadata::
 svn-remote.<name>.noMetadata::
-
-This gets rid of the 'git-svn-id:' lines at the end of every commit.
-
-If you lose your .git/svn/git-svn/.rev_db file, 'git-svn' will not
+       This gets rid of the 'git-svn-id:' lines at the end of every commit.
++
+If you lose your .git/svn/git-svn/.rev_db file, 'git svn' will not
 be able to rebuild it and you won't be able to fetch again,
 either.  This is fine for one-shot imports.
-
-The 'git-svn log' command will not work on repositories using
++
+The 'git svn log' command will not work on repositories using
 this, either.  Using this conflicts with the 'useSvmProps'
 option for (hopefully) obvious reasons.
 
 svn.useSvmProps::
 svn-remote.<name>.useSvmProps::
-
-This allows 'git-svn' to re-map repository URLs and UUIDs from
-mirrors created using SVN::Mirror (or svk) for metadata.
-
+       This allows 'git svn' to re-map repository URLs and UUIDs from
+       mirrors created using SVN::Mirror (or svk) for metadata.
++
 If an SVN revision has a property, "svm:headrev", it is likely
 that the revision was created by SVN::Mirror (also used by SVK).
 The property contains a repository UUID and a revision.  We want
@@ -596,23 +604,22 @@ svn-remote.<name>.useSvnsyncprops::
 
 svn-remote.<name>.rewriteRoot::
        This allows users to create repositories from alternate
-       URLs.  For example, an administrator could run 'git-svn' on the
+       URLs.  For example, an administrator could run 'git svn' on the
        server locally (accessing via file://) but wish to distribute
        the repository with a public http:// or svn:// URL in the
        metadata so users of it will see the public URL.
 
 svn.brokenSymlinkWorkaround::
-This disables potentially expensive checks to workaround broken symlinks
-checked into SVN by broken clients.  Set this option to "false" if you
-track a SVN repository with many empty blobs that are not symlinks.
-This option may be changed while "git-svn" is running and take effect on
-the next revision fetched.  If unset, git-svn assumes this option to be
-"true".
-
---
+       This disables potentially expensive checks to workaround
+       broken symlinks checked into SVN by broken clients.  Set this
+       option to "false" if you track a SVN repository with many
+       empty blobs that are not symlinks.  This option may be changed
+       while 'git svn' is running and take effect on the next
+       revision fetched.  If unset, 'git svn' assumes this option to
+       be "true".
 
 Since the noMetadata, rewriteRoot, useSvnsyncProps and useSvmProps
-options all affect the metadata generated and used by 'git-svn'; they
+options all affect the metadata generated and used by 'git svn'; they
 *must* be set in the configuration file before any history is imported
 and these settings should never be changed once they are set.
 
@@ -630,7 +637,7 @@ Tracking and contributing to the trunk of a Subversion-managed project:
        git svn clone http://svn.example.com/project/trunk
 # Enter the newly cloned directory:
        cd trunk
-# You should be on master branch, double-check with git-branch
+# You should be on master branch, double-check with 'git branch'
        git branch
 # Do some work and commit locally to git:
        git commit ...
@@ -661,12 +668,12 @@ Tracking and contributing to an entire Subversion-managed project
 # of dcommit/rebase/show-ignore should be the same as above.
 ------------------------------------------------------------------------
 
-The initial 'git-svn clone' can be quite time-consuming
+The initial 'git svn clone' can be quite time-consuming
 (especially for large Subversion repositories). If multiple
 people (or one person with multiple machines) want to use
-'git-svn' to interact with the same Subversion repository, you can
-do the initial 'git-svn clone' to a repository on a server and
-have each person clone that repository with 'git-clone':
+'git svn' to interact with the same Subversion repository, you can
+do the initial 'git svn clone' to a repository on a server and
+have each person clone that repository with 'git clone':
 
 ------------------------------------------------------------------------
 # Do the initial import on a server
@@ -680,7 +687,7 @@ have each person clone that repository with 'git-clone':
        git fetch
 # Create a local branch from one of the branches just fetched
        git checkout -b master FETCH_HEAD
-# Initialize git-svn locally (be sure to use the same URL and -T/-b/-t options as were used on server)
+# Initialize 'git svn' locally (be sure to use the same URL and -T/-b/-t options as were used on server)
        git svn init http://svn.example.com/project
 # Pull the latest changes from Subversion
        git svn rebase
@@ -689,7 +696,7 @@ have each person clone that repository with 'git-clone':
 REBASE VS. PULL/MERGE
 ---------------------
 
-Originally, 'git-svn' recommended that the 'remotes/git-svn' branch be
+Originally, 'git svn' recommended that the 'remotes/git-svn' branch be
 pulled or merged from.  This is because the author favored
 `git svn set-tree B` to commit a single head rather than the
 `git svn set-tree A..B` notation to commit multiple commits.
@@ -704,7 +711,7 @@ previous commits in SVN.
 DESIGN PHILOSOPHY
 -----------------
 Merge tracking in Subversion is lacking and doing branched development
-with Subversion can be cumbersome as a result.  While 'git-svn' can track
+with Subversion can be cumbersome as a result.  While 'git svn' can track
 copy history (including branches and tags) for repositories adopting a
 standard layout, it cannot yet represent merge history that happened
 inside git back upstream to SVN users.  Therefore it is advised that
@@ -715,25 +722,25 @@ CAVEATS
 -------
 
 For the sake of simplicity and interoperating with a less-capable system
-(SVN), it is recommended that all 'git-svn' users clone, fetch and dcommit
-directly from the SVN server, and avoid all 'git-clone'/'pull'/'merge'/'push'
+(SVN), it is recommended that all 'git svn' users clone, fetch and dcommit
+directly from the SVN server, and avoid all 'git clone'/'pull'/'merge'/'push'
 operations between git repositories and branches.  The recommended
 method of exchanging code between git branches and users is
-'git-format-patch' and 'git-am', or just 'dcommit'ing to the SVN repository.
+'git format-patch' and 'git am', or just 'dcommit'ing to the SVN repository.
 
-Running 'git-merge' or 'git-pull' is NOT recommended on a branch you
+Running 'git merge' or 'git pull' is NOT recommended on a branch you
 plan to 'dcommit' from.  Subversion does not represent merges in any
 reasonable or useful fashion; so users using Subversion cannot see any
 merges you've made.  Furthermore, if you merge or pull from a git branch
 that is a mirror of an SVN branch, 'dcommit' may commit to the wrong
 branch.
 
-'git-clone' does not clone branches under the refs/remotes/ hierarchy or
-any 'git-svn' metadata, or config.  So repositories created and managed with
-using 'git-svn' should use 'rsync' for cloning, if cloning is to be done
+'git clone' does not clone branches under the refs/remotes/ hierarchy or
+any 'git svn' metadata, or config.  So repositories created and managed with
+using 'git svn' should use 'rsync' for cloning, if cloning is to be done
 at all.
 
-Since 'dcommit' uses rebase internally, any git branches you 'git-push' to
+Since 'dcommit' uses rebase internally, any git branches you 'git push' to
 before 'dcommit' on will require forcing an overwrite of the existing ref
 on the remote repository.  This is generally considered bad practice,
 see the linkgit:git-push[1] documentation for details.
@@ -743,7 +750,7 @@ already dcommitted.  It is considered bad practice to --amend commits
 you've already pushed to a remote repository for other users, and
 dcommit with SVN is analogous to that.
 
-When using multiple --branches or --tags, 'git-svn' does not automatically
+When using multiple --branches or --tags, 'git svn' does not automatically
 handle name collisions (for example, if two branches from different paths have
 the same name, or if a branch and a tag have the same name).  In these cases,
 use 'init' to set up your git repository then, before your first 'fetch', edit
@@ -769,7 +776,7 @@ for git to detect them.
 CONFIGURATION
 -------------
 
-'git-svn' stores [svn-remote] configuration information in the
+'git svn' stores [svn-remote] configuration information in the
 repository .git/config file.  It is similar the core git
 [remote] sections except 'fetch' keys do not accept glob
 arguments; but they are instead handled by the 'branches'
@@ -790,7 +797,7 @@ Keep in mind that the '\*' (asterisk) wildcard of the local ref
 however the remote wildcard may be anywhere as long as it's an
 independent path component (surrounded by '/' or EOL).   This
 type of configuration is not automatically created by 'init' and
-should be manually entered with a text-editor or using 'git-config'.
+should be manually entered with a text-editor or using 'git config'.
 
 SEE ALSO
 --------
index fa733214ab0259dec1c866e9cb629dd0e7b69f3a..1118ce22dcfcbaf190bd7abc60c2b33c213acd06 100644 (file)
@@ -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.
index c8611632d1d501d57eb7000de0ec3c3b36810b80..d791a80819b39b5c3b07ace9d30f16c63f56fd2e 100644 (file)
@@ -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.
 \--::
index 6fa0310e05f2457ea95a8535460634d0f46cbce8..5fd5953e29d1514388421c0d729f46f8a54d3dba 100644 (file)
@@ -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],
diff --git a/Documentation/pt_BR/gittutorial.txt b/Documentation/pt_BR/gittutorial.txt
new file mode 100644 (file)
index 0000000..81e7ad7
--- /dev/null
@@ -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].
index 11eec941dff7302ffcd68eca3d6437d31c022ca9..bf66116d61af4f16cff59ed1563f38736a14ecce 100644 (file)
@@ -201,6 +201,10 @@ endif::git-rev-list[]
 
        Stop when a given path disappears from the tree.
 
+--merges::
+
+       Print only merge commits.
+
 --no-merges::
 
        Do not print commits with more than one parent.
index e3ddf912841298d6317a682a29cbaf628e86f156..55b728632cb7fa18de0d1dc070f5007acef053e0 100644 (file)
 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>
index 39cde784c947fae6a2f294caac1898b9199d769d..d7d9a9a063f6c7dbab5c9a14008b9a45e7f3379b 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.6.3.GIT
+DEF_VER=v1.6.4.GIT
 
 LF='
 '
index cdc40d05ca758714e506c0a403790e61fa82125e..daf4296706c4c9c524b6ec4434cadcf95881ea25 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -61,6 +61,8 @@ all::
 #
 # Define NO_LIBGEN_H if you don't have libgen.h.
 #
+# Define NEEDS_LIBGEN if your libgen needs -lgen when linking
+#
 # Define NO_SYS_SELECT_H if you don't have sys/select.h.
 #
 # Define NO_SYMLINK_HEAD if you never want .git/HEAD to be a symbolic link.
@@ -194,6 +196,8 @@ all::
 #
 # Define USE_NED_ALLOCATOR if you want to replace the platforms default
 # memory allocators with the nedmalloc allocator written by Niall Douglas.
+#
+# Define NO_REGEX if you have no or inferior regex support in your C library.
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -723,6 +727,8 @@ ifeq ($(uname_S),SunOS)
        NO_MEMMEM = YesPlease
        NO_MKDTEMP = YesPlease
        NO_MKSTEMPS = YesPlease
+       NO_REGEX = YesPlease
+       NO_EXTERNAL_GREP = YesPlease
        ifeq ($(uname_R),5.7)
                NEEDS_RESOLV = YesPlease
                NO_IPV6 = YesPlease
@@ -750,7 +756,7 @@ ifeq ($(uname_S),SunOS)
        endif
        INSTALL = /usr/ucb/install
        TAR = gtar
-       BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__
+       BASIC_CFLAGS += -D__EXTENSIONS__ -D__sun__ -DHAVE_ALLOCA_H
 endif
 ifeq ($(uname_O),Cygwin)
        NO_D_TYPE_IN_DIRENT = YesPlease
@@ -825,18 +831,31 @@ ifeq ($(uname_S),GNU)
        NO_STRLCPY=YesPlease
        NO_MKSTEMPS = YesPlease
 endif
+ifeq ($(uname_S),IRIX)
+       NO_SETENV = YesPlease
+       NO_UNSETENV = YesPlease
+       NO_STRCASESTR = YesPlease
+       NO_MEMMEM = YesPlease
+       NO_MKSTEMPS = YesPlease
+       NO_MKDTEMP = YesPlease
+       NO_MMAP = YesPlease
+       NO_EXTERNAL_GREP = UnfortunatelyYes
+       SNPRINTF_RETURNS_BOGUS = YesPlease
+       SHELL_PATH = /usr/gnu/bin/bash
+       NEEDS_LIBGEN = YesPlease
+endif
 ifeq ($(uname_S),IRIX64)
-       NO_IPV6=YesPlease
        NO_SETENV=YesPlease
+       NO_UNSETENV = YesPlease
        NO_STRCASESTR=YesPlease
        NO_MEMMEM = YesPlease
        NO_MKSTEMPS = YesPlease
-       NO_STRLCPY = YesPlease
-       NO_SOCKADDR_STORAGE=YesPlease
+       NO_MKDTEMP = YesPlease
+       NO_MMAP = YesPlease
+       NO_EXTERNAL_GREP = UnfortunatelyYes
+       SNPRINTF_RETURNS_BOGUS = YesPlease
        SHELL_PATH=/usr/gnu/bin/bash
-       BASIC_CFLAGS += -DPATH_MAX=1024
-       # for now, build 32-bit version
-       BASIC_LDFLAGS += -L/usr/lib32
+       NEEDS_LIBGEN = YesPlease
 endif
 ifeq ($(uname_S),HP-UX)
        NO_IPV6=YesPlease
@@ -884,9 +903,10 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        USE_NED_ALLOCATOR = YesPlease
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
-       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/regex -Icompat/fnmatch
+       NO_REGEX = YesPlease
+       COMPAT_CFLAGS += -D__USE_MINGW_ACCESS -DNOGDI -Icompat -Icompat/fnmatch
        COMPAT_CFLAGS += -DSTRIP_EXTENSION=\".exe\"
-       COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/regex/regex.o compat/winansi.o
+       COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o
        EXTLIBS += -lws2_32
        X = .exe
 ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
@@ -1015,6 +1035,9 @@ ifdef NEEDS_LIBICONV
        endif
        EXTLIBS += $(ICONV_LINK) -liconv
 endif
+ifdef NEEDS_LIBGEN
+       EXTLIBS += -lgen
+endif
 ifdef NEEDS_SOCKET
        EXTLIBS += -lsocket
 endif
@@ -1200,6 +1223,10 @@ endif
 ifdef UNRELIABLE_FSTAT
        BASIC_CFLAGS += -DUNRELIABLE_FSTAT
 endif
+ifdef NO_REGEX
+       COMPAT_CFLAGS += -Icompat/regex
+       COMPAT_OBJS += compat/regex/regex.o
+endif
 
 ifdef USE_NED_ALLOCATOR
        COMPAT_CFLAGS += -DUSE_NED_ALLOCATOR -DOVERRIDE_STRDUP -DNDEBUG -DREPLACE_SYSTEM_ALLOCATOR -Icompat/nedmalloc
@@ -1633,15 +1660,16 @@ ifneq (,$X)
 endif
        bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
        execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
-       { $(RM) "$$execdir/git-add$X" && \
+       { test "$$bindir/" = "$$execdir/" || \
+               { $(RM) "$$execdir/git$X" && \
                test -z "$(NO_CROSS_DIRECTORY_HARDLINKS)" && \
-               ln "$$bindir/git$X" "$$execdir/git-add$X" 2>/dev/null || \
-               cp "$$bindir/git$X" "$$execdir/git-add$X"; } && \
-       { for p in $(filter-out git-add$X,$(BUILT_INS)); do \
+               ln "$$bindir/git$X" "$$execdir/git$X" 2>/dev/null || \
+               cp "$$bindir/git$X" "$$execdir/git$X"; } ; } && \
+       { for p in $(BUILT_INS); do \
                $(RM) "$$execdir/$$p" && \
-               ln "$$execdir/git-add$X" "$$execdir/$$p" 2>/dev/null || \
-               ln -s "git-add$X" "$$execdir/$$p" 2>/dev/null || \
-               cp "$$execdir/git-add$X" "$$execdir/$$p" || exit; \
+               ln "$$execdir/git$X" "$$execdir/$$p" 2>/dev/null || \
+               ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
+               cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
          done; } && \
        ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
 
index f8e49a5070afc21fbd3db9320841300fe93c570f..b62449d2e22829f8bcbe404a48ff8f62c47eebd9 120000 (symlink)
--- 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
index 649f34f83365db3513c5166b897c4f033831444d..4bee0ba1ec6fcf49e88b874f0a415f4220117c0a 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -41,13 +41,13 @@ const char *make_absolute_path(const char *path)
 
                if (*buf) {
                        if (!*cwd && !getcwd(cwd, sizeof(cwd)))
-                               die ("Could not get current working directory");
+                               die_errno ("Could not get current working directory");
 
                        if (chdir(buf))
-                               die ("Could not switch to '%s'", buf);
+                               die_errno ("Could not switch to '%s'", buf);
                }
                if (!getcwd(buf, PATH_MAX))
-                       die ("Could not get current working directory");
+                       die_errno ("Could not get current working directory");
 
                if (last_elem) {
                        int len = strlen(buf);
@@ -63,7 +63,7 @@ const char *make_absolute_path(const char *path)
                if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
                        len = readlink(buf, next_buf, PATH_MAX);
                        if (len < 0)
-                               die ("Invalid symlink: %s", buf);
+                               die_errno ("Invalid symlink '%s'", buf);
                        if (PATH_MAX <= len)
                                die("symbolic link too long: %s", buf);
                        next_buf[len] = '\0';
@@ -75,7 +75,7 @@ const char *make_absolute_path(const char *path)
        }
 
        if (*cwd && chdir(cwd))
-               die ("Could not change back to '%s'", cwd);
+               die_errno ("Could not change back to '%s'", cwd);
 
        return buf;
 }
@@ -109,7 +109,7 @@ const char *make_nonrelative_path(const char *path)
        } else {
                const char *cwd = get_pwd_cwd();
                if (!cwd)
-                       die("Cannot determine the current working directory");
+                       die_errno("Cannot determine the current working directory");
                if (snprintf(buf, PATH_MAX, "%s/%s", cwd, path) >= PATH_MAX)
                        die("Too long path: %.*s", 60, path);
        }
index 52220df751aaacec08792c01ad83f2a1b27aae5f..7f20acb4b9e391bd383597ec554ec58b70979fe5 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -461,7 +461,7 @@ static void read_bisect_paths(struct argv_array *array)
        FILE *fp = fopen(filename, "r");
 
        if (!fp)
-               die("Could not open file '%s': %s", filename, strerror(errno));
+               die_errno("Could not open file '%s'", filename);
 
        while (strbuf_getline(&str, fp, '\n') != EOF) {
                char *quoted;
@@ -740,8 +740,7 @@ static void mark_expected_rev(char *bisect_rev_hex)
        int fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
 
        if (fd < 0)
-               die("could not create file '%s': %s",
-                   filename, strerror(errno));
+               die_errno("could not create file '%s'", filename);
 
        bisect_rev_hex[len] = '\n';
        write_or_die(fd, bisect_rev_hex, len + 1);
index 62030af4b51abbfb9c488dbf018770f3b3606789..05ef3f5c9ce6e1b55a7eb483a197fe9fd6e5437d 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -172,7 +172,7 @@ void create_branch(const char *head,
 
        lock = lock_any_ref_for_update(ref.buf, NULL, 0);
        if (!lock)
-               die("Failed to lock ref for update: %s.", strerror(errno));
+               die_errno("Failed to lock ref for update");
 
        if (reflog)
                log_all_ref_updates = 1;
@@ -188,7 +188,7 @@ void create_branch(const char *head,
                setup_tracking(name, real_ref, track);
 
        if (write_ref_sha1(lock, sha1, msg) < 0)
-               die("Failed to write ref: %s.", strerror(errno));
+               die_errno("Failed to write ref");
 
        strbuf_release(&ref);
        free(real_ref);
index 4e44148e05843e03befc167f6bdf3a1567d3f8ad..581a2a17480cc0ffa47a942ff58da4499aeac41d 100644 (file)
@@ -97,35 +97,6 @@ static void treat_gitlinks(const char **pathspec)
        }
 }
 
-static void fill_directory(struct dir_struct *dir, const char **pathspec,
-               int ignored_too)
-{
-       const char *path, *base;
-       int baselen;
-
-       /* Set up the default git porcelain excludes */
-       memset(dir, 0, sizeof(*dir));
-       if (!ignored_too) {
-               dir->flags |= DIR_COLLECT_IGNORED;
-               setup_standard_excludes(dir);
-       }
-
-       /*
-        * Calculate common prefix for the pathspec, and
-        * use that to optimize the directory walk
-        */
-       baselen = common_prefix(pathspec);
-       path = ".";
-       base = "";
-       if (baselen)
-               path = base = xmemdupz(*pathspec, baselen);
-
-       /* Read the directory and prune it */
-       read_directory(dir, path, base, baselen, pathspec);
-       if (pathspec)
-               prune_directory(dir, pathspec, baselen);
-}
-
 static void refresh(int verbose, const char **pathspec)
 {
        char *seen;
@@ -220,7 +191,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
        launch_editor(file, NULL, NULL);
 
        if (stat(file, &st))
-               die("Could not stat '%s'", file);
+               die_errno("Could not stat '%s'", file);
        if (!st.st_size)
                die("Empty patch. Aborted.");
 
@@ -343,9 +314,21 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                die("index file corrupt");
        treat_gitlinks(pathspec);
 
-       if (add_new_files)
+       if (add_new_files) {
+               int baselen;
+
+               /* Set up the default git porcelain excludes */
+               memset(&dir, 0, sizeof(dir));
+               if (!ignored_too) {
+                       dir.flags |= DIR_COLLECT_IGNORED;
+                       setup_standard_excludes(&dir);
+               }
+
                /* This picks up the paths that are not tracked */
-               fill_directory(&dir, pathspec, ignored_too);
+               baselen = fill_directory(&dir, pathspec);
+               if (pathspec)
+                       prune_directory(&dir, pathspec, baselen);
+       }
 
        if (refresh_only) {
                refresh(verbose, pathspec);
index 4cf819c7905d5997a6ce2a89b4ecce1176a4b045..39dc96ae0237235eb429b504ec28c9d66c32f4e1 100644 (file)
@@ -280,7 +280,7 @@ static void say_patch_name(FILE *output, const char *pre,
 static void read_patch_file(struct strbuf *sb, int fd)
 {
        if (strbuf_read(sb, fd, 0) < 0)
-               die("git apply: read returned %s", strerror(errno));
+               die_errno("git apply: failed to read");
 
        /*
         * Make sure that we have some slop in the buffer
@@ -457,6 +457,76 @@ static int guess_p_value(const char *nameline)
        return val;
 }
 
+/*
+ * 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
  *
@@ -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);
@@ -2823,8 +2903,8 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
        } else {
                if (!cached) {
                        if (lstat(path, &st) < 0)
-                               die("unable to stat newly created file %s",
-                                   path);
+                               die_errno("unable to stat newly created file '%s'",
+                                         path);
                        fill_stat_cache_info(ce, &st);
                }
                if (write_sha1_file(buf, size, blob_type, ce->sha1) < 0)
@@ -2864,7 +2944,7 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
        strbuf_release(&nbuf);
 
        if (close(fd) < 0)
-               die("closing file %s: %s", path, strerror(errno));
+               die_errno("closing file '%s'", path);
        return 0;
 }
 
@@ -2913,7 +2993,7 @@ static void create_one_file(char *path, unsigned mode, const char *buf, unsigned
                        ++nr;
                }
        }
-       die("unable to write file %s mode %o", path, mode);
+       die_errno("unable to write file '%s' mode %o", path, mode);
 }
 
 static void create_file(struct patch *patch)
@@ -3356,7 +3436,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
 
                fd = open(arg, O_RDONLY);
                if (fd < 0)
-                       die("can't open patch '%s': %s", arg, strerror(errno));
+                       die_errno("can't open patch '%s'", arg);
                read_stdin = 0;
                set_default_whitespace_mode(whitespace_option);
                errs |= apply_patch(fd, arg, options);
index 3c5a5a7822afebf87ef6eb1f89c7c54d76844161..f9a4bea41e95b9a34f190fc2b696c7394d0e0971 100644 (file)
@@ -13,10 +13,10 @@ static void create_output_file(const char *output_file)
 {
        int output_fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0666);
        if (output_fd < 0)
-               die("could not create archive file: %s ", output_file);
+               die_errno("could not create archive file '%s'", output_file);
        if (output_fd != 1) {
                if (dup2(output_fd, 1) < 0)
-                       die("could not redirect output");
+                       die_errno("could not redirect output");
                else
                        close(output_fd);
        }
index 0c2d29a43005e0afe7c0b0754499a37156e235ef..fd6ca51eebb2234be429b47b572ae2f30592ff4e 100644 (file)
@@ -2008,23 +2008,23 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
 
                if (contents_from) {
                        if (stat(contents_from, &st) < 0)
-                               die("Cannot stat %s", contents_from);
+                               die_errno("Cannot stat '%s'", contents_from);
                        read_from = contents_from;
                }
                else {
                        if (lstat(path, &st) < 0)
-                               die("Cannot lstat %s", path);
+                               die_errno("Cannot lstat '%s'", path);
                        read_from = path;
                }
                mode = canon_mode(st.st_mode);
                switch (st.st_mode & S_IFMT) {
                case S_IFREG:
                        if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
-                               die("cannot open or read %s", read_from);
+                               die_errno("cannot open or read '%s'", read_from);
                        break;
                case S_IFLNK:
                        if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
-                               die("cannot readlink %s", read_from);
+                               die_errno("cannot readlink '%s'", read_from);
                        break;
                default:
                        die("unsupported file type %s", read_from);
@@ -2035,7 +2035,7 @@ static struct commit *fake_working_tree_commit(const char *path, const char *con
                contents_from = "standard input";
                mode = 0;
                if (strbuf_read(&buf, 0, 0) < 0)
-                       die("read error %s from stdin", strerror(errno));
+                       die_errno("failed to read from stdin");
        }
        convert_to_git(path, buf.buf, buf.len, &buf, 0);
        origin->file.ptr = buf.buf;
@@ -2261,8 +2261,7 @@ parse_done:
        argc = parse_options_end(&ctx);
 
        if (revs_file && read_ancestry(revs_file))
-               die("reading graft file %s failed: %s",
-                   revs_file, strerror(errno));
+               die_errno("reading graft file '%s' failed", revs_file);
 
        if (cmd_is_annotate) {
                output_option |= OUTPUT_ANNOTATE_COMPAT;
@@ -2350,7 +2349,7 @@ parse_done:
 
                setup_work_tree();
                if (!has_string_in_work_tree(path))
-                       die("cannot stat path %s: %s", path, strerror(errno));
+                       die_errno("cannot stat path '%s'", path);
        }
 
        setup_revisions(argc, argv, &revs, NULL);
index 5687d6042ced29e60aa1999dc9d73eb6c38b8e77..1a03d5f356009de12364c320577e24853bec7959 100644 (file)
@@ -191,7 +191,7 @@ struct ref_item {
 
 struct ref_list {
        struct rev_info revs;
-       int index, alloc, maxwidth;
+       int index, alloc, maxwidth, verbose, abbrev;
        struct ref_item *list;
        struct commit_list *with_commit;
        int kinds;
@@ -240,21 +240,24 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
        if (ARRAY_SIZE(ref_kind) <= i)
                return 0;
 
-       commit = lookup_commit_reference_gently(sha1, 1);
-       if (!commit)
-               return error("branch '%s' does not point at a commit", refname);
-
-       /* Filter with with_commit if specified */
-       if (!is_descendant_of(commit, ref_list->with_commit))
-               return 0;
-
        /* Don't add types the caller doesn't want */
        if ((kind & ref_list->kinds) == 0)
                return 0;
 
-       if (merge_filter != NO_FILTER)
-               add_pending_object(&ref_list->revs,
-                                  (struct object *)commit, refname);
+       commit = NULL;
+       if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) {
+               commit = lookup_commit_reference_gently(sha1, 1);
+               if (!commit)
+                       return error("branch '%s' does not point at a commit", refname);
+
+               /* Filter with with_commit if specified */
+               if (!is_descendant_of(commit, ref_list->with_commit))
+                       return 0;
+
+               if (merge_filter != NO_FILTER)
+                       add_pending_object(&ref_list->revs,
+                                          (struct object *)commit, refname);
+       }
 
        /* Resize buffer */
        if (ref_list->index >= ref_list->alloc) {
@@ -415,18 +418,38 @@ static int calc_maxwidth(struct ref_list *refs)
        return w;
 }
 
+
+static void show_detached(struct ref_list *ref_list)
+{
+       struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1);
+
+       if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) {
+               struct ref_item item;
+               item.name = xstrdup("(no branch)");
+               item.len = strlen(item.name);
+               item.kind = REF_LOCAL_BRANCH;
+               item.dest = NULL;
+               item.commit = head_commit;
+               if (item.len > ref_list->maxwidth)
+                       ref_list->maxwidth = item.len;
+               print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, "");
+               free(item.name);
+       }
+}
+
 static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
 {
        int i;
        struct ref_list ref_list;
-       struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1);
 
        memset(&ref_list, 0, sizeof(ref_list));
        ref_list.kinds = kinds;
+       ref_list.verbose = verbose;
+       ref_list.abbrev = abbrev;
        ref_list.with_commit = with_commit;
        if (merge_filter != NO_FILTER)
                init_revisions(&ref_list.revs, NULL);
-       for_each_ref(append_ref, &ref_list);
+       for_each_rawref(append_ref, &ref_list);
        if (merge_filter != NO_FILTER) {
                struct commit *filter;
                filter = lookup_commit_reference_gently(merge_filter_ref, 0);
@@ -442,19 +465,8 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
        qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
 
        detached = (detached && (kinds & REF_LOCAL_BRANCH));
-       if (detached && head_commit &&
-           is_descendant_of(head_commit, with_commit)) {
-               struct ref_item item;
-               item.name = xstrdup("(no branch)");
-               item.len = strlen(item.name);
-               item.kind = REF_LOCAL_BRANCH;
-               item.dest = NULL;
-               item.commit = head_commit;
-               if (item.len > ref_list.maxwidth)
-                       ref_list.maxwidth = item.len;
-               print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1, "");
-               free(item.name);
-       }
+       if (detached)
+               show_detached(&ref_list);
 
        for (i = 0; i < ref_list.index; i++) {
                int current = !detached &&
index 1c1b6d26e9987800d2a414e49e4f371a4277b96e..2d8c735d4881a005e4aa5006d9781b71631bb0af 100644 (file)
@@ -33,7 +33,6 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        int ignored_only = 0, baselen = 0, config_set = 0, errors = 0;
        struct strbuf directory = STRBUF_INIT;
        struct dir_struct dir;
-       const char *path, *base;
        static const char **pathspec;
        struct strbuf buf = STRBUF_INIT;
        const char *qname;
@@ -78,16 +77,7 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
        pathspec = get_pathspec(prefix, argv);
        read_cache();
 
-       /*
-        * Calculate common prefix for the pathspec, and
-        * use that to optimize the directory walk
-        */
-       baselen = common_prefix(pathspec);
-       path = ".";
-       base = "";
-       if (baselen)
-               path = base = xmemdupz(*pathspec, baselen);
-       read_directory(&dir, path, base, baselen, pathspec);
+       fill_directory(&dir, pathspec);
 
        if (pathspec)
                seen = xmalloc(argc > 0 ? argc : 1);
index 2ceacb7b3f34c78e64ed047833d8af5a061f065f..32dea74d78381ca1dffdac7f88e45a89a9825104 100644 (file)
@@ -220,13 +220,13 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
 
        dir = opendir(src->buf);
        if (!dir)
-               die("failed to open %s", src->buf);
+               die_errno("failed to open '%s'", src->buf);
 
        if (mkdir(dest->buf, 0777)) {
                if (errno != EEXIST)
-                       die("failed to create directory %s", dest->buf);
+                       die_errno("failed to create directory '%s'", dest->buf);
                else if (stat(dest->buf, &buf))
-                       die("failed to stat %s", dest->buf);
+                       die_errno("failed to stat '%s'", dest->buf);
                else if (!S_ISDIR(buf.st_mode))
                        die("%s exists and is not a directory", dest->buf);
        }
@@ -252,17 +252,16 @@ static void copy_or_link_directory(struct strbuf *src, struct strbuf *dest)
                }
 
                if (unlink(dest->buf) && errno != ENOENT)
-                       die("failed to unlink %s: %s",
-                           dest->buf, strerror(errno));
+                       die_errno("failed to unlink '%s'", dest->buf);
                if (!option_no_hardlinks) {
                        if (!link(src->buf, dest->buf))
                                continue;
                        if (option_local)
-                               die("failed to create link %s", dest->buf);
+                               die_errno("failed to create link '%s'", dest->buf);
                        option_no_hardlinks = 1;
                }
                if (copy_file(dest->buf, src->buf, 0666))
-                       die("failed to copy file to %s", dest->buf);
+                       die_errno("failed to copy file to '%s'", dest->buf);
        }
        closedir(dir);
 }
@@ -420,11 +419,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (!option_bare) {
                junk_work_tree = work_tree;
                if (safe_create_leading_directories_const(work_tree) < 0)
-                       die("could not create leading directories of '%s': %s",
-                                       work_tree, strerror(errno));
+                       die_errno("could not create leading directories of '%s'",
+                                 work_tree);
                if (!dest_exists && mkdir(work_tree, 0755))
-                       die("could not create work tree dir '%s': %s.",
-                                       work_tree, strerror(errno));
+                       die_errno("could not create work tree dir '%s'.",
+                                 work_tree);
                set_git_work_tree(work_tree);
        }
        junk_git_dir = git_dir;
index 0453425c471f1d6793bc7f5f59d17e9d285ddfc6..64670777312c71d45b10f72a1efe9fceae3e3b89 100644 (file)
@@ -124,7 +124,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
        }
 
        if (strbuf_read(&buffer, 0, 0) < 0)
-               die("git commit-tree: read returned %s", strerror(errno));
+               die_errno("git commit-tree: failed to read");
 
        if (!commit_tree(buffer.buf, tree_sha1, parents, commit_sha1, NULL)) {
                printf("%s\n", sha1_to_hex(commit_sha1));
index 41e222d267ca952c3bc0c8d6b1e19c0171df0c62..4bcce06fbffdf10ec701dfbc0b6b90a11513f89e 100644 (file)
@@ -434,12 +434,12 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
                if (isatty(0))
                        fprintf(stderr, "(reading log message from standard input)\n");
                if (strbuf_read(&sb, 0, 0) < 0)
-                       die("could not read log from standard input");
+                       die_errno("could not read log from standard input");
                hook_arg1 = "message";
        } else if (logfile) {
                if (strbuf_read_file(&sb, logfile, 0) < 0)
-                       die("could not read log file '%s': %s",
-                           logfile, strerror(errno));
+                       die_errno("could not read log file '%s'",
+                                 logfile);
                hook_arg1 = "message";
        } else if (use_message) {
                buffer = strstr(use_message_buffer, "\n\n");
@@ -450,16 +450,15 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
                hook_arg2 = use_message;
        } else if (!stat(git_path("MERGE_MSG"), &statbuf)) {
                if (strbuf_read_file(&sb, git_path("MERGE_MSG"), 0) < 0)
-                       die("could not read MERGE_MSG: %s", strerror(errno));
+                       die_errno("could not read MERGE_MSG");
                hook_arg1 = "merge";
        } else if (!stat(git_path("SQUASH_MSG"), &statbuf)) {
                if (strbuf_read_file(&sb, git_path("SQUASH_MSG"), 0) < 0)
-                       die("could not read SQUASH_MSG: %s", strerror(errno));
+                       die_errno("could not read SQUASH_MSG");
                hook_arg1 = "squash";
        } else if (template_file && !stat(template_file, &statbuf)) {
                if (strbuf_read_file(&sb, template_file, 0) < 0)
-                       die("could not read %s: %s",
-                           template_file, strerror(errno));
+                       die_errno("could not read '%s'", template_file);
                hook_arg1 = "template";
        }
 
@@ -472,8 +471,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
 
        fp = fopen(git_path(commit_editmsg), "w");
        if (fp == NULL)
-               die("could not open %s: %s",
-                   git_path(commit_editmsg), strerror(errno));
+               die_errno("could not open '%s'", git_path(commit_editmsg));
 
        if (cleanup_mode != CLEANUP_NONE)
                stripspace(&sb, 0);
@@ -497,7 +495,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix)
        }
 
        if (fwrite(sb.buf, 1, sb.len, fp) < sb.len)
-               die("could not write commit template: %s", strerror(errno));
+               die_errno("could not write commit template");
 
        strbuf_release(&sb);
 
@@ -940,8 +938,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                pptr = &commit_list_insert(lookup_commit(head_sha1), pptr)->next;
                fp = fopen(git_path("MERGE_HEAD"), "r");
                if (fp == NULL)
-                       die("could not open %s for reading: %s",
-                           git_path("MERGE_HEAD"), strerror(errno));
+                       die_errno("could not open '%s' for reading",
+                                 git_path("MERGE_HEAD"));
                while (strbuf_getline(&m, fp, '\n') != EOF) {
                        unsigned char sha1[20];
                        if (get_sha1_hex(m.buf, sha1) < 0)
@@ -952,8 +950,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                strbuf_release(&m);
                if (!stat(git_path("MERGE_MODE"), &statbuf)) {
                        if (strbuf_read_file(&sb, git_path("MERGE_MODE"), 0) < 0)
-                               die("could not read MERGE_MODE: %s",
-                                               strerror(errno));
+                               die_errno("could not read MERGE_MODE");
                        if (!strcmp(sb.buf, "no-ff"))
                                allow_fast_forward = 0;
                }
@@ -967,8 +964,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        /* Finally, get the commit message */
        strbuf_reset(&sb);
        if (strbuf_read_file(&sb, git_path(commit_editmsg), 0) < 0) {
+               int saved_errno = errno;
                rollback_index_files();
-               die("could not read commit message");
+               die("could not read commit message: %s", strerror(saved_errno));
        }
 
        /* Truncate the message just before the diff, if any. */
index 60915f91caff02997222d18bc625d242d95c6bb1..a2d656edb383da47fb3622f1f7c2d1524285ac00 100644 (file)
@@ -383,8 +383,8 @@ int cmd_config(int argc, const char **argv, const char *unused_prefix)
                check_argc(argc, 0, 0);
                if (git_config(show_all_config, NULL) < 0) {
                        if (config_exclusive_filename)
-                               die("unable to read config file %s: %s",
-                                   config_exclusive_filename, strerror(errno));
+                               die_errno("unable to read config file '%s'",
+                                         config_exclusive_filename);
                        else
                                die("error processing config file(s)");
                }
index 7a662980d102a275fceb8299bba0b84b41a8f9cb..df67a733ae5c91b0b4278c31c12a7432c0ec601e 100644 (file)
@@ -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) {
                /*
index d75d69bf5774ffd402bbeec47b9a0e0800554d63..ffcdd055ca0b9b30bec2ce1f22e348ec15d58c81 100644 (file)
@@ -70,7 +70,7 @@ static int builtin_diff_b_f(struct rev_info *revs,
                usage(builtin_diff_usage);
 
        if (lstat(path, &st))
-               die("'%s': %s", path, strerror(errno));
+               die_errno("failed to stat '%s'", path);
        if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)))
                die("'%s': not a regular file or symlink", path);
 
@@ -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--;
index 6cef8103127a8748acb64ca7ec7a7a95fb639cd3..b0a4029c94d1bdb1c673fe604cdbfec93df875aa 100644 (file)
@@ -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;
 
@@ -119,7 +138,7 @@ static void handle_object(const unsigned char *sha1)
 
        printf("blob\nmark :%"PRIu32"\ndata %lu\n", last_idnum, size);
        if (size && fwrite(buf, size, 1, stdout) != 1)
-               die ("Could not write blob %s", sha1_to_hex(sha1));
+               die_errno ("Could not write blob '%s'", sha1_to_hex(sha1));
        printf("\n");
 
        show_progress();
@@ -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 : "");
@@ -428,21 +499,27 @@ static void export_marks(char *file)
        uint32_t mark;
        struct object_decoration *deco = idnums.hash;
        FILE *f;
+       int e = 0;
 
        f = fopen(file, "w");
        if (!f)
-               error("Unable to open marks file %s for writing", file);
+               error("Unable to open marks file %s for writing.", file);
 
        for (i = 0; i < idnums.size; i++) {
                if (deco->base && deco->base->type == 1) {
                        mark = ptr_to_mark(deco->decoration);
-                       fprintf(f, ":%"PRIu32" %s\n", mark,
-                               sha1_to_hex(deco->base->sha1));
+                       if (fprintf(f, ":%"PRIu32" %s\n", mark,
+                               sha1_to_hex(deco->base->sha1)) < 0) {
+                           e = 1;
+                           break;
+                       }
                }
                deco++;
        }
 
-       if (ferror(f) || fclose(f))
+       e |= ferror(f);
+       e |= fclose(f);
+       if (e)
                error("Unable to write marks file %s.", file);
 }
 
@@ -451,7 +528,7 @@ static void import_marks(char *input_file)
        char line[512];
        FILE *f = fopen(input_file, "r");
        if (!f)
-               die("cannot read %s: %s", input_file, strerror(errno));
+               die_errno("cannot read '%s'", input_file);
 
        while (fgets(line, sizeof(line), f)) {
                uint32_t mark;
@@ -498,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()
        };
 
@@ -514,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)
@@ -524,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);
index 29356d25db910c6d90df46da87aa374467611350..3dbdf7a2887002f0d7a67a1db35bd3c72d7a9d30 100644 (file)
@@ -8,7 +8,7 @@ static char *get_stdin(void)
 {
        struct strbuf buf = STRBUF_INIT;
        if (strbuf_read(&buf, 0, 1024) < 0) {
-               die("error reading standard input: %s", strerror(errno));
+               die_errno("error reading standard input");
        }
        return strbuf_detach(&buf, NULL);
 }
index cd5eb9aff530fcb9629bd561bc774e2b9ad69afc..817dd6bff0bf0ddc9a3d00191bb3c422300b878b 100644 (file)
@@ -400,14 +400,14 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 
 /*
  * We would want to bypass the object transfer altogether if
- * everything we are going to fetch already exists and connected
+ * everything we are going to fetch already exists and is connected
  * locally.
  *
- * The refs we are going to fetch are in to_fetch (nr_heads in
- * total).  If running
+ * The refs we are going to fetch are in ref_map.  If running
  *
- *  $ git rev-list --objects to_fetch[0] to_fetch[1] ... --not --all
+ *  $ git rev-list --objects --stdin --not --all
  *
+ * (feeding all the refs in ref_map on its standard input)
  * does not error out, that means everything reachable from the
  * refs we are going to fetch exists and is connected to some of
  * our existing refs.
@@ -416,8 +416,9 @@ static int quickfetch(struct ref *ref_map)
 {
        struct child_process revlist;
        struct ref *ref;
-       char **argv;
-       int i, err;
+       int err;
+       const char *argv[] = {"rev-list",
+               "--quiet", "--objects", "--stdin", "--not", "--all", NULL};
 
        /*
         * If we are deepening a shallow clone we already have these
@@ -429,34 +430,46 @@ static int quickfetch(struct ref *ref_map)
        if (depth)
                return -1;
 
-       for (i = 0, ref = ref_map; ref; ref = ref->next)
-               i++;
-       if (!i)
+       if (!ref_map)
                return 0;
 
-       argv = xmalloc(sizeof(*argv) * (i + 6));
-       i = 0;
-       argv[i++] = xstrdup("rev-list");
-       argv[i++] = xstrdup("--quiet");
-       argv[i++] = xstrdup("--objects");
-       for (ref = ref_map; ref; ref = ref->next)
-               argv[i++] = xstrdup(sha1_to_hex(ref->old_sha1));
-       argv[i++] = xstrdup("--not");
-       argv[i++] = xstrdup("--all");
-       argv[i++] = NULL;
-
        memset(&revlist, 0, sizeof(revlist));
-       revlist.argv = (const char**)argv;
+       revlist.argv = argv;
        revlist.git_cmd = 1;
-       revlist.no_stdin = 1;
        revlist.no_stdout = 1;
        revlist.no_stderr = 1;
-       err = run_command(&revlist);
+       revlist.in = -1;
+
+       err = start_command(&revlist);
+       if (err) {
+               error("could not run rev-list");
+               return err;
+       }
+
+       /*
+        * If rev-list --stdin encounters an unknown commit, it terminates,
+        * which will cause SIGPIPE in the write loop below.
+        */
+       sigchain_push(SIGPIPE, SIG_IGN);
+
+       for (ref = ref_map; ref; ref = ref->next) {
+               if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 ||
+                   write_in_full(revlist.in, "\n", 1) < 0) {
+                       if (errno != EPIPE && errno != EINVAL)
+                               error("failed write to rev-list: %s", strerror(errno));
+                       err = -1;
+                       break;
+               }
+       }
+
+       if (close(revlist.in)) {
+               error("failed to close rev-list's stdin: %s", strerror(errno));
+               err = -1;
+       }
+
+       sigchain_pop(SIGPIPE);
 
-       for (i = 0; argv[i]; i++)
-               free(argv[i]);
-       free(argv);
-       return err;
+       return finish_command(&revlist) || err;
 }
 
 static int fetch_refs(struct transport *transport, struct ref *ref_map)
index fbf9582e667dfd1e69027b35c3f307f3c7a08a99..9d524000b5ba4d9c7566edd5756b68d728ec362b 100644 (file)
@@ -368,12 +368,11 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix)
        if (inpath && strcmp(inpath, "-")) {
                in = fopen(inpath, "r");
                if (!in)
-                       die("cannot open %s", inpath);
+                       die_errno("cannot open '%s'", inpath);
        }
 
        if (strbuf_read(&input, fileno(in), 0) < 0)
-               die("could not read input file %s", strerror(errno));
-
+               die_errno("could not read input file");
        ret = fmt_merge_msg(merge_summary, &input, &output);
        if (ret)
                return ret;
index e077e72dea94892e2f74ddd6be528b02bc6ccea4..b3d38fa277b3d0024b9c46a62cf15bc2448cc1e7 100644 (file)
@@ -217,7 +217,7 @@ static void check_unreachable_object(struct object *obj)
                                return;
                        }
                        if (!(f = fopen(filename, "w")))
-                               die("Could not open %s", filename);
+                               die_errno("Could not open '%s'", filename);
                        if (obj->type == OBJ_BLOB) {
                                enum object_type type;
                                unsigned long size;
@@ -225,15 +225,15 @@ static void check_unreachable_object(struct object *obj)
                                                &type, &size);
                                if (buf) {
                                        if (fwrite(buf, size, 1, f) != 1)
-                                               die("Could not write %s: %s",
-                                                   filename, strerror(errno));
+                                               die_errno("Could not write '%s'",
+                                                         filename);
                                        free(buf);
                                }
                        } else
                                fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
                        if (fclose(f))
-                               die("Could not finish %s: %s",
-                                   filename, strerror(errno));
+                               die_errno("Could not finish '%s'",
+                                         filename);
                }
                return;
        }
index 73fc922c4995369c0ca86c01200e2a0ea39161ab..f477659100fdc63bff5938b4c96f28eaefc07460 100644 (file)
@@ -11,6 +11,7 @@
 #include "tree-walk.h"
 #include "builtin.h"
 #include "parse-options.h"
+#include "userdiff.h"
 #include "grep.h"
 
 #ifndef NO_EXTERNAL_GREP
@@ -30,6 +31,12 @@ static int grep_config(const char *var, const char *value, void *cb)
 {
        struct grep_opt *opt = cb;
 
+       switch (userdiff_config(var, value)) {
+       case 0: break;
+       case -1: return -1;
+       default: return 0;
+       }
+
        if (!strcmp(var, "color.grep")) {
                opt->color = git_config_colorbool(var, value, -1);
                return 0;
@@ -278,6 +285,17 @@ static int flush_grep(struct grep_opt *opt,
                argc -= 2;
        }
 
+       if (opt->pre_context || opt->post_context) {
+               /*
+                * grep handles hunk marks between files, but we need to
+                * do that ourselves between multiple calls.
+                */
+               if (opt->show_hunk_mark)
+                       write_or_die(1, "--\n", 3);
+               else
+                       opt->show_hunk_mark = 1;
+       }
+
        status = exec_grep(argc, argv);
 
        if (kept_0) {
@@ -594,7 +612,7 @@ static int file_callback(const struct option *opt, const char *arg, int unset)
 
        patterns = fopen(arg, "r");
        if (!patterns)
-               die("'%s': %s", arg, strerror(errno));
+               die_errno("cannot open '%s'", arg);
        while (strbuf_getline(&sb, patterns, '\n') == 0) {
                /* ignore empty line like grep does */
                if (sb.len == 0)
@@ -710,6 +728,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        "show <n> context lines after matches"),
                OPT_NUMBER_CALLBACK(&opt, "shortcut for -C NUM",
                        context_callback),
+               OPT_BOOLEAN('p', "show-function", &opt.funcname,
+                       "show a line with the function name before matches"),
                OPT_GROUP(""),
                OPT_CALLBACK('f', NULL, &opt, "file",
                        "read patterns from file", file_callback),
@@ -778,7 +798,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                argc--;
        }
 
-       if (opt.color && !opt.color_external)
+       if ((opt.color && !opt.color_external) || opt.funcname)
                external_grep_allowed = 0;
        if (!opt.pattern_list)
                die("no pattern given.");
index d1fa12a59efb34256b2cc80b03c637cc844d84ff..dd84caecbc2a07bca90c8524157d50a8fd5ae316 100644 (file)
@@ -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"
@@ -61,20 +62,20 @@ static void copy_templates_1(char *path, int baselen,
                memcpy(template + template_baselen, de->d_name, namelen+1);
                if (lstat(path, &st_git)) {
                        if (errno != ENOENT)
-                               die("cannot stat %s", path);
+                               die_errno("cannot stat '%s'", path);
                }
                else
                        exists = 1;
 
                if (lstat(template, &st_template))
-                       die("cannot stat template %s", template);
+                       die_errno("cannot stat template '%s'", template);
 
                if (S_ISDIR(st_template.st_mode)) {
                        DIR *subdir = opendir(template);
                        int baselen_sub = baselen + namelen;
                        int template_baselen_sub = template_baselen + namelen;
                        if (!subdir)
-                               die("cannot opendir %s", template);
+                               die_errno("cannot opendir '%s'", template);
                        path[baselen_sub++] =
                                template[template_baselen_sub++] = '/';
                        path[baselen_sub] =
@@ -91,16 +92,17 @@ static void copy_templates_1(char *path, int baselen,
                        int len;
                        len = readlink(template, lnk, sizeof(lnk));
                        if (len < 0)
-                               die("cannot readlink %s", template);
+                               die_errno("cannot readlink '%s'", template);
                        if (sizeof(lnk) <= len)
                                die("insanely long symlink %s", template);
                        lnk[len] = 0;
                        if (symlink(lnk, path))
-                               die("cannot symlink %s %s", lnk, path);
+                               die_errno("cannot symlink '%s' '%s'", lnk, path);
                }
                else if (S_ISREG(st_template.st_mode)) {
                        if (copy_file(path, template, st_template.st_mode))
-                               die("cannot copy %s to %s", template, path);
+                               die_errno("cannot copy '%s' to '%s'", template,
+                                         path);
                }
                else
                        error("ignoring template %s", template);
@@ -350,7 +352,7 @@ static int guess_repository_type(const char *git_dir)
        if (!strcmp(".", git_dir))
                return 1;
        if (!getcwd(cwd, sizeof(cwd)))
-               die("cannot tell cwd");
+               die_errno("cannot tell cwd");
        if (!strcmp(git_dir, cwd))
                return 1;
        /*
@@ -369,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.
@@ -383,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)
@@ -440,11 +485,11 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
                if (!git_work_tree_cfg) {
                        git_work_tree_cfg = xcalloc(PATH_MAX, 1);
                        if (!getcwd(git_work_tree_cfg, PATH_MAX))
-                               die ("Cannot access current working directory.");
+                               die_errno ("Cannot access current working directory");
                }
                if (access(get_git_work_tree(), X_OK))
-                       die ("Cannot access work tree '%s'",
-                            get_git_work_tree());
+                       die_errno ("Cannot access work tree '%s'",
+                                  get_git_work_tree());
        }
 
        set_git_dir(make_absolute_path(git_dir));
index 44f9a27daec612db6d0f0e502c99d5e5ecaaf842..3817bf11864dbe5bff2c1004abec2b97210a0c36 100644 (file)
@@ -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;
@@ -1013,8 +1025,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                if (use_stdout)
                        die("standard output, or directory, which one?");
                if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
-                       die("Could not create directory %s",
-                           output_directory);
+                       die_errno("Could not create directory '%s'",
+                                 output_directory);
        }
 
        if (rev.pending.nr == 1) {
index 2312866605c4140ac4e5a9f5bb963ca5ab5c6da3..f473220502027b4f9e6ed9a17ffafd42538add80 100644 (file)
@@ -161,12 +161,7 @@ static void show_files(struct dir_struct *dir, const char *prefix)
 
        /* For cached/deleted files we don't need to even do the readdir */
        if (show_others || show_killed) {
-               const char *path = ".", *base = "";
-               int baselen = prefix_len;
-
-               if (baselen)
-                       path = base = prefix;
-               read_directory(dir, path, base, baselen, pathspec);
+               fill_directory(dir, pathspec);
                if (show_others)
                        show_other_files(dir);
                if (show_killed)
index 71f3b3b8741e505fc652e6c74c75972f19211f71..ad5f6b593df45f01360f3daa8b37d024ee793e9e 100644 (file)
@@ -81,7 +81,7 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
 
        fd = open(name, O_WRONLY | O_CREAT | O_EXCL, 0666);
        if (fd < 0)
-               die("cannot open output file %s", name);
+               die_errno("cannot open output file '%s'", name);
        output = fdopen(fd, "w");
 
        /* Copy it out, while searching for a line that begins with
@@ -91,7 +91,7 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
                int is_partial = len && buf[len-1] != '\n';
 
                if (fwrite(buf, 1, len, output) != len)
-                       die("cannot write output");
+                       die_errno("cannot write output");
 
                len = read_line_with_nul(buf, sizeof(buf), mbox);
                if (len == 0) {
@@ -99,7 +99,7 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
                                status = 1;
                                break;
                        }
-                       die("cannot read mbox");
+                       die_errno("cannot read mbox");
                }
                if (!is_partial && !is_bare && is_from_line(buf, len))
                        break; /* done with one message */
index a6ec2f7ab76db27afbb63aacf3bba60b4e73b3f6..54e7ec22370ce63150ddc93ebe252bea09f5064a 100644 (file)
@@ -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
 };
 
index 96ecaf4e484639c844d48884ae2fd559d5e9a352..0b12fb31558d1cb59f9e2a8ab049ea944fe3060e 100644 (file)
@@ -268,7 +268,7 @@ static void squash_message(void)
        printf("Squash commit -- not updating HEAD\n");
        fd = open(git_path("SQUASH_MSG"), O_WRONLY | O_CREAT, 0666);
        if (fd < 0)
-               die("Could not write to %s", git_path("SQUASH_MSG"));
+               die_errno("Could not write to '%s'", git_path("SQUASH_MSG"));
 
        init_revisions(&rev, NULL);
        rev.ignore_merges = 1;
@@ -294,9 +294,9 @@ static void squash_message(void)
                        NULL, NULL, rev.date_mode, 0);
        }
        if (write(fd, out.buf, out.len) < 0)
-               die("Writing SQUASH_MSG: %s", strerror(errno));
+               die_errno("Writing SQUASH_MSG");
        if (close(fd))
-               die("Finishing SQUASH_MSG: %s", strerror(errno));
+               die_errno("Finishing SQUASH_MSG");
        strbuf_release(&out);
 }
 
@@ -428,8 +428,8 @@ static void merge_name(const char *remote, struct strbuf *msg)
 
                fp = fopen(git_path("FETCH_HEAD"), "r");
                if (!fp)
-                       die("could not open %s for reading: %s",
-                               git_path("FETCH_HEAD"), strerror(errno));
+                       die_errno("could not open '%s' for reading",
+                                 git_path("FETCH_HEAD"));
                strbuf_getline(&line, fp, '\n');
                fclose(fp);
                ptr = strstr(line.buf, "\tnot-for-merge\t");
@@ -764,7 +764,8 @@ static int suggest_conflicts(void)
 
        fp = fopen(git_path("MERGE_MSG"), "a");
        if (!fp)
-               die("Could not open %s for writing", git_path("MERGE_MSG"));
+               die_errno("Could not open '%s' for writing",
+                         git_path("MERGE_MSG"));
        fprintf(fp, "\nConflicts:\n");
        for (pos = 0; pos < active_nr; pos++) {
                struct cache_entry *ce = active_cache[pos];
@@ -1189,27 +1190,29 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                                sha1_to_hex(j->item->object.sha1));
                fd = open(git_path("MERGE_HEAD"), O_WRONLY | O_CREAT, 0666);
                if (fd < 0)
-                       die("Could open %s for writing",
-                               git_path("MERGE_HEAD"));
+                       die_errno("Could not open '%s' for writing",
+                                 git_path("MERGE_HEAD"));
                if (write_in_full(fd, buf.buf, buf.len) != buf.len)
-                       die("Could not write to %s", git_path("MERGE_HEAD"));
+                       die_errno("Could not write to '%s'", git_path("MERGE_HEAD"));
                close(fd);
                strbuf_addch(&merge_msg, '\n');
                fd = open(git_path("MERGE_MSG"), O_WRONLY | O_CREAT, 0666);
                if (fd < 0)
-                       die("Could open %s for writing", git_path("MERGE_MSG"));
+                       die_errno("Could not open '%s' for writing",
+                                 git_path("MERGE_MSG"));
                if (write_in_full(fd, merge_msg.buf, merge_msg.len) !=
                        merge_msg.len)
-                       die("Could not write to %s", git_path("MERGE_MSG"));
+                       die_errno("Could not write to '%s'", git_path("MERGE_MSG"));
                close(fd);
                fd = open(git_path("MERGE_MODE"), O_WRONLY | O_CREAT | O_TRUNC, 0666);
                if (fd < 0)
-                       die("Could open %s for writing", git_path("MERGE_MODE"));
+                       die_errno("Could not open '%s' for writing",
+                                 git_path("MERGE_MODE"));
                strbuf_reset(&buf);
                if (!allow_fast_forward)
                        strbuf_addf(&buf, "no-ff");
                if (write_in_full(fd, buf.buf, buf.len) != buf.len)
-                       die("Could not write to %s", git_path("MERGE_MODE"));
+                       die_errno("Could not write to '%s'", git_path("MERGE_MODE"));
                close(fd);
        }
 
index 68f47384dc1cc6404d4db73b6dbea8bceab38df9..b592c367b2cc016a50db6d49948e9efbbf0e8f2f 100644 (file)
@@ -205,7 +205,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                        printf("Renaming %s to %s\n", src, dst);
                if (!show_only && mode != INDEX &&
                                rename(src, dst) < 0 && !ignore_errors)
-                       die ("renaming %s failed: %s", src, strerror(errno));
+                       die_errno ("renaming '%s' failed", src);
 
                if (mode == WORKING_DIRECTORY)
                        continue;
index 941cc2d73cf5ee6791a0cee8c409dcdb9756b448..ef4bf6bc14aadffcd85999c2eca59e5a0e7e6451 100644 (file)
@@ -536,11 +536,9 @@ static void write_pack_file(void)
                                 base_name, sha1_to_hex(sha1));
                        free_pack_by_name(tmpname);
                        if (adjust_perm(pack_tmp_name, mode))
-                               die("unable to make temporary pack file readable: %s",
-                                   strerror(errno));
+                               die_errno("unable to make temporary pack file readable");
                        if (rename(pack_tmp_name, tmpname))
-                               die("unable to rename temporary pack file: %s",
-                                   strerror(errno));
+                               die_errno("unable to rename temporary pack file");
 
                        /*
                         * Packs are runtime accessed in their mtime
@@ -566,11 +564,9 @@ static void write_pack_file(void)
                        snprintf(tmpname, sizeof(tmpname), "%s-%s.idx",
                                 base_name, sha1_to_hex(sha1));
                        if (adjust_perm(idx_tmp_name, mode))
-                               die("unable to make temporary index file readable: %s",
-                                   strerror(errno));
+                               die_errno("unable to make temporary index file readable");
                        if (rename(idx_tmp_name, tmpname))
-                               die("unable to rename temporary index file: %s",
-                                   strerror(errno));
+                               die_errno("unable to rename temporary index file");
 
                        free(idx_tmp_name);
                        free(pack_tmp_name);
@@ -1879,7 +1875,7 @@ static void read_object_list_from_stdin(void)
                        if (!ferror(stdin))
                                die("fgets returned NULL, not EOF, not error!");
                        if (errno != EINTR)
-                               die("fgets: %s", strerror(errno));
+                               die_errno("fgets");
                        clearerr(stdin);
                        continue;
                }
@@ -2259,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);
        }
 
index 00590b1c3c2cceda7a75537c8680a96cd2ef84d4..be99eb0ac4162411d63d10003fa80e12206b8cc6 100644 (file)
@@ -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;
 }
index 7be12399b6d95c63efd9102df7967dca61655063..1d92e22f0aef914217c6a68e2597426bb529e4ba 100644 (file)
@@ -10,7 +10,7 @@
 #include "parse-options.h"
 
 static const char * const push_usage[] = {
-       "git push [--all | --mirror] [--dry-run] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]",
+       "git push [--all | --mirror] [--dry-run] [--porcelain] [--tags] [--receive-pack=<git-receive-pack>] [--repo=<repository>] [-f | --force] [-v] [<repository> <refspec>...]",
        NULL,
 };
 
@@ -64,36 +64,11 @@ static void setup_push_tracking(void)
        add_refspec(refspec.buf);
 }
 
-static const char *warn_unconfigured_push_msg[] = {
-       "You did not specify any refspecs to push, and the current remote",
-       "has not configured any push refspecs. The default action in this",
-       "case is to push all matching refspecs, that is, all branches",
-       "that exist both locally and remotely will be updated.  This may",
-       "not necessarily be what you want to happen.",
-       "",
-       "You can specify what action you want to take in this case, and",
-       "avoid seeing this message again, by configuring 'push.default' to:",
-       "  'nothing'  : Do not push anything",
-       "  'matching' : Push all matching branches (default)",
-       "  'tracking' : Push the current branch to whatever it is tracking",
-       "  'current'  : Push the current branch"
-};
-
-static void warn_unconfigured_push(void)
-{
-       int i;
-       for (i = 0; i < ARRAY_SIZE(warn_unconfigured_push_msg); i++)
-               warning("%s", warn_unconfigured_push_msg[i]);
-}
-
 static void setup_default_push_refspecs(void)
 {
        git_config(git_default_config, NULL);
        switch (push_default) {
-       case PUSH_DEFAULT_UNSPECIFIED:
-               warn_unconfigured_push();
-               /* fallthrough */
-
+       default:
        case PUSH_DEFAULT_MATCHING:
                add_refspec(":");
                break;
@@ -200,6 +175,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                            (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
                OPT_BOOLEAN( 0 , "tags", &tags, "push tags"),
                OPT_BIT( 0 , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
+               OPT_BIT( 0,  "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN),
                OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE),
                OPT_BOOLEAN( 0 , "thin", &thin, "use thin pack"),
                OPT_STRING( 0 , "receive-pack", &receivepack, "receive-pack", "receive pack program"),
index 82e25eaa0758d8b9584592f4072f81eeccb0bf1d..9c2d634d6d04c3ce08e1352adcba31b527fc08b1 100644 (file)
@@ -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)
index ddfdf5a3cbc79003dfbad14af8d1f047e3594aa6..95198c5de41072bfb8adf7f29d9bbb44eec665ac 100644 (file)
@@ -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)
 {
index 112d622cda6f74f9752627d9adf3a1cdb04851c3..45bead65451e87a4b564e59fed3518a4de9198e9 100644 (file)
@@ -301,7 +301,7 @@ static const char *skipspaces(const char *s)
 
 static int cmd_parseopt(int argc, const char **argv, const char *prefix)
 {
-       static int keep_dashdash = 0;
+       static int keep_dashdash = 0, stop_at_non_option = 0;
        static char const * const parseopt_usage[] = {
                "git rev-parse --parseopt [options] -- [<args>...]",
                NULL
@@ -309,6 +309,9 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
        static struct option parseopt_opts[] = {
                OPT_BOOLEAN(0, "keep-dashdash", &keep_dashdash,
                                        "keep the `--` passed as an arg"),
+               OPT_BOOLEAN(0, "stop-at-non-option", &stop_at_non_option,
+                                       "stop parsing after the "
+                                       "first non-option argument"),
                OPT_END(),
        };
 
@@ -394,7 +397,8 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
        ALLOC_GROW(opts, onb + 1, osz);
        memset(opts + onb, 0, sizeof(opts[onb]));
        argc = parse_options(argc, argv, prefix, opts, usage,
-                            keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0);
+                       keep_dashdash ? PARSE_OPT_KEEP_DASHDASH : 0 |
+                       stop_at_non_option ? PARSE_OPT_STOP_AT_NON_OPTION : 0);
 
        strbuf_addf(&parsed, " --");
        sq_quote_argv(&parsed, argv, 0);
@@ -592,7 +596,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                        continue;
                                }
                                if (!getcwd(cwd, PATH_MAX))
-                                       die("unable to get current working directory");
+                                       die_errno("unable to get current working directory");
                                printf("%s/.git\n", cwd);
                                continue;
                        }
index c87115af30abd9515599586af860ccd07b96b264..151aa6a981832954120359f7c953015525b530d8 100644 (file)
@@ -135,7 +135,7 @@ static void add_to_msg(const char *string)
 {
        int len = strlen(string);
        if (write_in_full(msg_fd, string, len) < 0)
-               die ("Could not write to MERGE_MSG");
+               die_errno ("Could not write to MERGE_MSG");
 }
 
 static void add_message_to_msg(const char *message)
index 0cc491271846214d73d55a4f528c8854e0460c8f..57975dbcfd7c0dbcba03b88a1bf403f1ec5f528c 100644 (file)
@@ -257,7 +257,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!removed)
-                               die("git rm: %s: %s", path, strerror(errno));
+                               die_errno("git rm: '%s'", path);
                }
        }
 
index c375a3dbde0a75af592c8881965f255f66b599d1..47fb9f7baa9ad9c070e1a6d9c245300ba2402ce5 100644 (file)
@@ -59,7 +59,7 @@ static int pack_objects(int fd, struct ref *refs, struct extra_have_objects *ext
        po.out = fd;
        po.git_cmd = 1;
        if (start_command(&po))
-               die("git pack-objects failed (%s)", strerror(errno));
+               die_errno("git pack-objects failed");
 
        /*
         * We feed the pack-objects we just spawned with revision
index 01bea3b583b53f4930a1ab17d64b1b714f58099c..3510a86e38d2821880060d4faa554c016a9f95e2 100644 (file)
@@ -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,
index dc76c5090f2367426201b37c5b13c2a5cbf00de2..c46550c9c01da8fe74145e16656a5163a7e4f07b 100644 (file)
@@ -4,12 +4,18 @@
 #include "object.h"
 #include "tag.h"
 #include "string-list.h"
+#include "parse-options.h"
 
-static const char show_ref_usage[] = "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<length>]] [--abbrev[=<length>]] [--tags] [--heads] [--] [pattern*] < ref-list";
+static const char * const show_ref_usage[] = {
+       "git show-ref [-q|--quiet] [--verify] [-h|--head] [-d|--dereference] [-s|--hash[=<n>]] [--abbrev[=<n>]] [--tags] [--heads] [--] [pattern*] ",
+       "git show-ref --exclude-existing[=pattern] < ref-list",
+       NULL
+};
 
-static int deref_tags = 0, show_head = 0, tags_only = 0, heads_only = 0,
-       found_match = 0, verify = 0, quiet = 0, hash_only = 0, abbrev = 0;
+static int deref_tags, show_head, tags_only, heads_only, found_match, verify,
+          quiet, hash_only, abbrev, exclude_arg;
 static const char **pattern;
+static const char *exclude_existing_arg;
 
 static void show_one(const char *refname, const unsigned char *sha1)
 {
@@ -150,79 +156,60 @@ static int exclude_existing(const char *match)
        return 0;
 }
 
+static int hash_callback(const struct option *opt, const char *arg, int unset)
+{
+       hash_only = 1;
+       /* Use full length SHA1 if no argument */
+       if (!arg)
+               return 0;
+       return parse_opt_abbrev_cb(opt, arg, unset);
+}
+
+static int exclude_existing_callback(const struct option *opt, const char *arg,
+                                    int unset)
+{
+       exclude_arg = 1;
+       *(const char **)opt->value = arg;
+       return 0;
+}
+
+static int help_callback(const struct option *opt, const char *arg, int unset)
+{
+       return -1;
+}
+
+static const struct option show_ref_options[] = {
+       OPT_BOOLEAN(0, "tags", &tags_only, "only show tags (can be combined with heads)"),
+       OPT_BOOLEAN(0, "heads", &heads_only, "only show heads (can be combined with tags)"),
+       OPT_BOOLEAN(0, "verify", &verify, "stricter reference checking, "
+                   "requires exact ref path"),
+       OPT_BOOLEAN('h', "head", &show_head, "show the HEAD reference"),
+       OPT_BOOLEAN('d', "dereference", &deref_tags,
+                   "dereference tags into object IDs"),
+       { OPTION_CALLBACK, 's', "hash", &abbrev, "n",
+         "only show SHA1 hash using <n> digits",
+         PARSE_OPT_OPTARG, &hash_callback },
+       OPT__ABBREV(&abbrev),
+       OPT__QUIET(&quiet),
+       { OPTION_CALLBACK, 0, "exclude-existing", &exclude_existing_arg,
+         "pattern", "show refs from stdin that aren't in local repository",
+         PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback },
+       { OPTION_CALLBACK, 0, "help-all", NULL, NULL, "show usage",
+         PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
+       OPT_END()
+};
+
 int cmd_show_ref(int argc, const char **argv, const char *prefix)
 {
-       int i;
+       argc = parse_options(argc, argv, prefix, show_ref_options,
+                            show_ref_usage, PARSE_OPT_NO_INTERNAL_HELP);
 
-       for (i = 1; i < argc; i++) {
-               const char *arg = argv[i];
-               if (*arg != '-') {
-                       pattern = argv + i;
-                       break;
-               }
-               if (!strcmp(arg, "--")) {
-                       pattern = argv + i + 1;
-                       if (!*pattern)
-                               pattern = NULL;
-                       break;
-               }
-               if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) {
-                       quiet = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-h") || !strcmp(arg, "--head")) {
-                       show_head = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-d") || !strcmp(arg, "--dereference")) {
-                       deref_tags = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "-s") || !strcmp(arg, "--hash")) {
-                       hash_only = 1;
-                       continue;
-               }
-               if (!prefixcmp(arg, "--hash=") ||
-                   (!prefixcmp(arg, "--abbrev") &&
-                    (arg[8] == '=' || arg[8] == '\0'))) {
-                       if (arg[2] != 'h' && !arg[8])
-                               /* --abbrev only */
-                               abbrev = DEFAULT_ABBREV;
-                       else {
-                               /* --hash= or --abbrev= */
-                               char *end;
-                               if (arg[2] == 'h') {
-                                       hash_only = 1;
-                                       arg += 7;
-                               }
-                               else
-                                       arg += 9;
-                               abbrev = strtoul(arg, &end, 10);
-                               if (*end || abbrev > 40)
-                                       usage(show_ref_usage);
-                               if (abbrev < MINIMUM_ABBREV)
-                                       abbrev = MINIMUM_ABBREV;
-                       }
-                       continue;
-               }
-               if (!strcmp(arg, "--verify")) {
-                       verify = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--tags")) {
-                       tags_only = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--heads")) {
-                       heads_only = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--exclude-existing"))
-                       return exclude_existing(NULL);
-               if (!prefixcmp(arg, "--exclude-existing="))
-                       return exclude_existing(arg + 19);
-               usage(show_ref_usage);
-       }
+       if (exclude_arg)
+               return exclude_existing(exclude_existing_arg);
+
+       pattern = argv;
+       if (!*pattern)
+               pattern = NULL;
 
        if (verify) {
                if (!pattern)
index d6e3896c006796ccca12c00de45e36583387f05b..1fd2205d530ad510a18ff5a441ca7b5cb1cc34a6 100644 (file)
@@ -78,7 +78,7 @@ int cmd_stripspace(int argc, const char **argv, const char *prefix)
                strip_comments = 1;
 
        if (strbuf_read(&buf, 0, 1024) < 0)
-               die("could not read the input");
+               die_errno("could not read the input");
 
        stripspace(&buf, strip_comments);
 
index 080e04a8fc6fc1fc3f10f59a4c1f1aa067b81aa1..a51a6d1ea21412f151238e48984bc13110fcbc6c 100644 (file)
@@ -308,8 +308,7 @@ static void create_tag(const unsigned char *object, const char *tag,
                path = git_pathdup("TAG_EDITMSG");
                fd = open(path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
                if (fd < 0)
-                       die("could not create file '%s': %s",
-                                               path, strerror(errno));
+                       die_errno("could not create file '%s'", path);
 
                if (!is_null_sha1(prev))
                        write_tag_body(fd, prev);
@@ -443,11 +442,11 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                else {
                        if (!strcmp(msgfile, "-")) {
                                if (strbuf_read(&buf, 0, 1024) < 0)
-                                       die("cannot read %s", msgfile);
+                                       die_errno("cannot read '%s'", msgfile);
                        } else {
                                if (strbuf_read_file(&buf, msgfile, 1024) < 0)
-                                       die("could not open or read '%s': %s",
-                                               msgfile, strerror(errno));
+                                       die_errno("could not open or read '%s'",
+                                               msgfile);
                        }
                }
        }
index f88e7219367c309dbc3d3e7ce2db32666703a91d..8b3a35e12da2cca8b632d4139bc2c5ba2409d322 100644 (file)
@@ -91,7 +91,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
 
        n = write_in_full(1, content + 11, 41);
        if (n < 41)
-               die("git get-tar-commit-id: write error");
+               die_errno("git get-tar-commit-id: write error");
 
        return 0;
 }
index 7e3ea73006ecdc74c03580ac7297be4ebee174b3..557148a693c058f51222cb3d996c309791d43d8b 100644 (file)
@@ -68,7 +68,7 @@ static void *fill(int min)
                if (ret <= 0) {
                        if (!ret)
                                die("early EOF");
-                       die("read error on input: %s", strerror(errno));
+                       die_errno("read error on input");
                }
                len += ret;
        } while (len < min);
index 0ee0a9af60b0601fe0e6db98ec582e059f5e9064..b5bd28e9598ab63696f241511f3a9cc032dbed60 100644 (file)
@@ -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;
 }
index 7f7fda42f9b7ab272ff2b3fa4ad1984a79f11ce0..9f482c29f516bde84023f401b28b133c1e605333 100644 (file)
 #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: */
index 3a24ce8157be7c4209d831027693653b8d65ca13..b223af416fee5fc219fbcca7afa2b9e03feaa7d0 100644 (file)
@@ -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) {
index 16a65dfac11eccc68acb4a0ed472761f3f84f9c4..d91743775dfbe98d99d8ab25a270af320c8b984e 100644 (file)
@@ -329,7 +329,8 @@ static int update_one(struct cache_tree *it,
                        entlen = pathlen - baselen;
                }
                if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1))
-                       return error("invalid object %s", sha1_to_hex(sha1));
+                       return error("invalid object %06o %s for '%.*s'",
+                               mode, sha1_to_hex(sha1), entlen+baselen, path);
 
                if (ce->ce_flags & CE_REMOVE)
                        continue; /* entry being removed */
diff --git a/cache.h b/cache.h
index 871c9844e88c8e73aa1bab0e079d0399bef1b11d..e6c7f3307d09e592aa5bcc63fe6d372aa54a5da3 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -543,7 +543,6 @@ enum rebase_setup_type {
 };
 
 enum push_default_type {
-       PUSH_DEFAULT_UNSPECIFIED = -1,
        PUSH_DEFAULT_NOTHING = 0,
        PUSH_DEFAULT_MATCHING,
        PUSH_DEFAULT_TRACKING,
@@ -561,6 +560,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);
@@ -744,7 +745,17 @@ struct checkout {
 };
 
 extern int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath);
+
+struct cache_def {
+       char path[PATH_MAX + 1];
+       int len;
+       int flags;
+       int track_flags;
+       int prefix_len_stat_func;
+};
+
 extern int has_symlink_leading_path(const char *name, int len);
+extern int threaded_has_symlink_leading_path(struct cache_def *, const char *, int);
 extern int has_symlink_or_noent_leading_path(const char *name, int len);
 extern int has_dirs_only_path(const char *name, int len, int prefix_len);
 extern void invalidate_lstat_cache(const char *name, int len);
index 60d03676bbd0dba7be79c7098d7cae553962ca98..5b63af1eeb71907f23117bc552fa7a4019f09722 100644 (file)
@@ -80,6 +80,7 @@ struct lline {
 /* Lines surviving in the merge result */
 struct sline {
        struct lline *lost_head, **lost_tail;
+       struct lline *next_lost;
        char *bol;
        int len;
        /* bit 0 up to (N-1) are on if the parent has this line (i.e.
@@ -121,18 +122,12 @@ static void append_lost(struct sline *sline, int n, const char *line, int len)
 
        /* Check to see if we can squash things */
        if (sline->lost_head) {
-               struct lline *last_one = NULL;
-               /* We cannot squash it with earlier one */
-               for (lline = sline->lost_head;
-                    lline;
-                    lline = lline->next)
-                       if (lline->parent_map & this_mask)
-                               last_one = lline;
-               lline = last_one ? last_one->next : sline->lost_head;
+               lline = sline->next_lost;
                while (lline) {
                        if (lline->len == len &&
                            !memcmp(lline->line, line, len)) {
                                lline->parent_map |= this_mask;
+                               sline->next_lost = lline->next;
                                return;
                        }
                        lline = lline->next;
@@ -147,6 +142,7 @@ static void append_lost(struct sline *sline, int n, const char *line, int len)
        lline->line[len] = 0;
        *sline->lost_tail = lline;
        sline->lost_tail = &lline->next;
+       sline->next_lost = NULL;
 }
 
 struct combine_diff_state {
@@ -168,25 +164,28 @@ static void consume_line(void *state_, char *line, unsigned long len)
                                      &state->nb, &state->nn))
                        return;
                state->lno = state->nb;
-               if (!state->nb)
-                       /* @@ -1,2 +0,0 @@ to remove the
-                        * first two lines...
-                        */
-                       state->nb = 1;
-               if (state->nn == 0)
+               if (state->nn == 0) {
                        /* @@ -X,Y +N,0 @@ removed Y lines
                         * that would have come *after* line N
                         * in the result.  Our lost buckets hang
                         * to the line after the removed lines,
+                        *
+                        * Note that this is correct even when N == 0,
+                        * in which case the hunk removes the first
+                        * line in the file.
                         */
                        state->lost_bucket = &state->sline[state->nb];
-               else
+                       if (!state->nb)
+                               state->nb = 1;
+               } else {
                        state->lost_bucket = &state->sline[state->nb-1];
+               }
                if (!state->sline[state->nb-1].p_lno)
                        state->sline[state->nb-1].p_lno =
                                xcalloc(state->num_parent,
                                        sizeof(unsigned long));
                state->sline[state->nb-1].p_lno[state->n] = state->ob;
+               state->lost_bucket->next_lost = state->lost_bucket->lost_head;
                return;
        }
        if (!state->lost_bucket)
@@ -746,7 +745,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
 
                        done = read_in_full(fd, result, len);
                        if (done < 0)
-                               die("read error '%s'", elem->path);
+                               die_errno("read error '%s'", elem->path);
                        else if (done < len)
                                die("early EOF '%s'", elem->path);
 
index aa3b35b6a86891ac9d0628e20a6a46d506bf7700..e2bcbe814936989e7a86018e46ed6ca86f4c1f10 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -50,7 +50,6 @@ struct commit *lookup_commit(const unsigned char *sha1)
 
 static unsigned long parse_commit_date(const char *buf, const char *tail)
 {
-       unsigned long date;
        const char *dateptr;
 
        if (buf + 6 >= tail)
@@ -73,10 +72,7 @@ static unsigned long parse_commit_date(const char *buf, const char *tail)
        if (buf >= tail)
                return 0;
        /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
-       date = strtoul(dateptr, NULL, 10);
-       if (date == ULONG_MAX)
-               date = 0;
-       return date;
+       return strtoul(dateptr, NULL, 10);
 }
 
 static struct commit_graft **commit_graft;
@@ -266,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)
index 1682273c12ab042d73fa32caf30d18fb13ef85e3..e87edeab0c6b9579ecbd9bc5a9a11c3522d21ccf 100644 (file)
--- 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;
                }
index dd6045131880111bb09306cb3e48330a0e5acdac..67b12f73a1bb795207082973701ece8009167f1c 100644 (file)
@@ -34,6 +34,7 @@ NO_LIBGEN_H=@NO_LIBGEN_H@
 NEEDS_LIBICONV=@NEEDS_LIBICONV@
 NEEDS_SOCKET=@NEEDS_SOCKET@
 NEEDS_RESOLV=@NEEDS_RESOLV@
+NEEDS_LIBGEN=@NEEDS_LIBGEN@
 NO_SYS_SELECT_H=@NO_SYS_SELECT_H@
 NO_D_INO_IN_DIRENT=@NO_D_INO_IN_DIRENT@
 NO_D_TYPE_IN_DIRENT=@NO_D_TYPE_IN_DIRENT@
index 1885674e396d31cc625d34d029835e15b279dc8d..3f1922d0bf66abdbcad03dfd9981ca5293a4b860 100644 (file)
@@ -342,9 +342,8 @@ GIT_STASH_FLAGS($OPENSSLDIR)
 AC_CHECK_LIB([crypto], [SHA1_Init],
 [NEEDS_SSL_WITH_CRYPTO=],
 [AC_CHECK_LIB([ssl], [SHA1_Init],
- [NEEDS_SSL_WITH_CRYPTO=YesPlease
-  NEEDS_SSL_WITH_CRYPTO=],
- [NO_OPENSSL=YesPlease])])
+ [NEEDS_SSL_WITH_CRYPTO=YesPlease],
+ [NEEDS_SSL_WITH_CRYPTO= NO_OPENSSL=YesPlease])])
 
 GIT_UNSTASH_FLAGS($OPENSSLDIR)
 
@@ -479,12 +478,18 @@ test -n "$NEEDS_SOCKET" && LIBS="$LIBS -lsocket"
 # Define NEEDS_RESOLV if linking with -lnsl and/or -lsocket is not enough.
 # Notably on Solaris hstrerror resides in libresolv and on Solaris 7
 # inet_ntop and inet_pton additionally reside there.
-AC_CHECK_LIB([resolv], [hstrerror],
+AC_CHECK_LIB([c], [hstrerror],
 [NEEDS_RESOLV=],
 [NEEDS_RESOLV=YesPlease])
 AC_SUBST(NEEDS_RESOLV)
 test -n "$NEEDS_RESOLV" && LIBS="$LIBS -lresolv"
 
+AC_CHECK_LIB([c], [basename],
+[NEEDS_LIBGEN=],
+[NEEDS_LIBGEN=YesPlease])
+AC_SUBST(NEEDS_LIBGEN)
+test -n "$NEEDS_LIBGEN" && LIBS="$LIBS -lgen"
+
 ## Checks for header files.
 AC_MSG_NOTICE([CHECKS for header files])
 #
index ddb71e26fe8e465dbfa60fa948a01d5ff5c0818b..745b5fb78be02086d7d94aaaeaa40930150430f9 100755 (executable)
 #       GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed,
 #       then a '$' will be shown next to the branch name.
 #
+#       If you would like to see if there're untracked files, then you can
+#       set GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're
+#       untracked files, then a '%' will be shown next to the branch name.
+#
 # To submit patches:
 #
 #    *) Read Documentation/SubmittingPatches
@@ -132,6 +136,7 @@ __git_ps1 ()
                local w
                local i
                local s
+               local u
                local c
 
                if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
@@ -156,12 +161,18 @@ __git_ps1 ()
                        if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
                                git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
                        fi
+
+                       if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then
+                          if [ -n "$(git ls-files --others --exclude-standard)" ]; then
+                             u="%"
+                          fi
+                       fi
                fi
 
                if [ -n "${1-}" ]; then
-                       printf "$1" "$c${b##refs/heads/}$w$i$s$r"
+                       printf "$1" "$c${b##refs/heads/}$w$i$s$u$r"
                else
-                       printf " (%s)" "$c${b##refs/heads/}$w$i$s$r"
+                       printf " (%s)" "$c${b##refs/heads/}$w$i$s$u$r"
                fi
        fi
 }
@@ -1114,7 +1125,7 @@ _git_ls_tree ()
 __git_log_common_options="
        --not --all
        --branches --tags --remotes
-       --first-parent --no-merges
+       --first-parent --merges --no-merges
        --max-count=
        --max-age= --since= --after=
        --min-age= --until= --before=
@@ -1165,7 +1176,7 @@ _git_log ()
                        $__git_log_shortlog_options
                        $__git_log_gitk_options
                        --root --topo-order --date-order --reverse
-                       --follow
+                       --follow --full-diff
                        --abbrev-commit --abbrev=
                        --relative-date --date=
                        --pretty= --format= --oneline
@@ -1357,11 +1368,12 @@ __git_config_get_set_variables ()
                c=$((--c))
        done
 
-       for i in $(git --git-dir="$(__gitdir)" config $config_file --list \
-                       2>/dev/null); do
-               case "$i" in
-               *.*)
-                       echo "${i/=*/}"
+       git --git-dir="$(__gitdir)" config $config_file --list 2>/dev/null |
+       while read line
+       do
+               case "$line" in
+               *.*=*)
+                       echo "${line/=*/}"
                        ;;
                esac
        done
index eace9c18eb1d17075836694ce664a009f3e02038..8c70ad8b7f462c7f87224798485a5ea46a865622 100644 (file)
@@ -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 ()
index 7b03204ed18500756ba55818f0808b52db68d048..2a6839d81ee970cb799859c7a833f8f8cb780864 100755 (executable)
@@ -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
index 2ddb12a0b70da87afe6fa8a33dce08c6c8ae7f71..4d50cc5ce18c24a1dc853d3050062b864fe0b943 100644 (file)
@@ -26,7 +26,7 @@ static void flush(struct sha1file *f, void * buf, unsigned int count)
                }
                if (!ret)
                        die("sha1 file '%s' write error. Out of diskspace", f->name);
-               die("sha1 file '%s' write error (%s)", f->name, strerror(errno));
+               die_errno("sha1 file '%s' write error", f->name);
        }
 }
 
@@ -55,8 +55,7 @@ int sha1close(struct sha1file *f, unsigned char *result, unsigned int flags)
                if (flags & CSUM_FSYNC)
                        fsync_or_die(f->fd, f->name);
                if (close(f->fd))
-                       die("%s: sha1 file error on close (%s)",
-                           f->name, strerror(errno));
+                       die_errno("%s: sha1 file error on close", f->name);
                fd = 0;
        } else
                fd = f->fd;
index 76a400557d0e8c615d6cceae511e3c7684131f04..1b5ada6648da66d14dec6c399898916920582852 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -902,7 +902,7 @@ static int service_loop(int socknum, int *socklist)
                                        case ECONNABORTED:
                                                continue;
                                        default:
-                                               die("accept returned %s", strerror(errno));
+                                               die_errno("accept returned");
                                        }
                                }
                                handle(incoming, (struct sockaddr *)&ss, sslen);
@@ -918,7 +918,7 @@ static void sanitize_stdfds(void)
        while (fd != -1 && fd < 2)
                fd = dup(fd);
        if (fd == -1)
-               die("open /dev/null or dup failed: %s", strerror(errno));
+               die_errno("open /dev/null or dup failed");
        if (fd > 2)
                close(fd);
 }
@@ -929,12 +929,12 @@ static void daemonize(void)
                case 0:
                        break;
                case -1:
-                       die("fork failed: %s", strerror(errno));
+                       die_errno("fork failed");
                default:
                        exit(0);
        }
        if (setsid() == -1)
-               die("setsid failed: %s", strerror(errno));
+               die_errno("setsid failed");
        close(0);
        close(1);
        close(2);
@@ -945,9 +945,9 @@ static void store_pid(const char *path)
 {
        FILE *f = fopen(path, "w");
        if (!f)
-               die("cannot open pid file %s: %s", path, strerror(errno));
+               die_errno("cannot open pid file '%s'", path);
        if (fprintf(f, "%"PRIuMAX"\n", (uintmax_t) getpid()) < 0 || fclose(f) != 0)
-               die("failed to write pid file %s: %s", path, strerror(errno));
+               die_errno("failed to write pid file '%s'", path);
 }
 
 static int serve(char *listen_addr, int listen_port, struct passwd *pass, gid_t gid)
@@ -1147,8 +1147,7 @@ int main(int argc, char **argv)
                socklen_t slen = sizeof(ss);
 
                if (!freopen("/dev/null", "w", stderr))
-                       die("failed to redirect stderr to /dev/null: %s",
-                           strerror(errno));
+                       die_errno("failed to redirect stderr to /dev/null");
 
                if (getpeername(0, peer, &slen))
                        peer = NULL;
diff --git a/diff.c b/diff.c
index 43835d756c2c2c7ec579e1c38804fe33a706944f..cd35e0c2d7e29012e24cc8f5f49d9d351420b56e 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1603,6 +1603,7 @@ static void builtin_diff(const char *name_a,
                        free(mf1.ptr);
                if (textconv_two)
                        free(mf2.ptr);
+               xdiff_clear_find_func(&xecfg);
        }
 
  free_ab_and_return:
@@ -1975,14 +1976,14 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
        fd = git_mkstemps(temp->tmp_path, PATH_MAX, template.buf,
                        strlen(base) + 1);
        if (fd < 0)
-               die("unable to create temp-file: %s", strerror(errno));
+               die_errno("unable to create temp-file");
        if (convert_to_working_tree(path,
                        (const char *)blob, (size_t)size, &buf)) {
                blob = buf.buf;
                size = buf.len;
        }
        if (write_in_full(fd, blob, size) != size)
-               die("unable to write temp-file");
+               die_errno("unable to write temp-file");
        close(fd);
        temp->name = temp->tmp_path;
        strcpy(temp->hex, sha1_to_hex(sha1));
@@ -2021,12 +2022,12 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
                if (lstat(name, &st) < 0) {
                        if (errno == ENOENT)
                                goto not_a_valid_file;
-                       die("stat(%s): %s", name, strerror(errno));
+                       die_errno("stat(%s)", name);
                }
                if (S_ISLNK(st.st_mode)) {
                        struct strbuf sb = STRBUF_INIT;
                        if (strbuf_readlink(&sb, name, st.st_size) < 0)
-                               die("readlink(%s)", name);
+                               die_errno("readlink(%s)", name);
                        prep_temp_blob(name, temp, sb.buf, sb.len,
                                       (one->sha1_valid ?
                                        one->sha1 : null_sha1),
@@ -2219,7 +2220,7 @@ static void diff_fill_sha1_info(struct diff_filespec *one)
                                return;
                        }
                        if (lstat(one->path, &st) < 0)
-                               die("stat %s", one->path);
+                               die_errno("stat '%s'", one->path);
                        if (index_path(one->sha1, one->path, &st, 0))
                                die("cannot hash %s", one->path);
                }
diff --git a/dir.c b/dir.c
index bbfcb566e63310d38848b0b76076fb051a7446c8..e05b850acf69867f2b931e7ca2f7430a9e7fc22d 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -14,12 +14,11 @@ struct path_simplify {
        const char *path;
 };
 
-static int read_directory_recursive(struct dir_struct *dir,
-       const char *path, const char *base, int baselen,
+static int read_directory_recursive(struct dir_struct *dir, const char *path, int len,
        int check_only, const struct path_simplify *simplify);
-static int get_dtype(struct dirent *de, const char *path);
+static int get_dtype(struct dirent *de, const char *path, int len);
 
-int common_prefix(const char **pathspec)
+static int common_prefix(const char **pathspec)
 {
        const char *path, *slash, *next;
        int prefix;
@@ -52,6 +51,26 @@ int common_prefix(const char **pathspec)
        return prefix;
 }
 
+int fill_directory(struct dir_struct *dir, const char **pathspec)
+{
+       const char *path;
+       int len;
+
+       /*
+        * Calculate common prefix for the pathspec, and
+        * use that to optimize the directory walk
+        */
+       len = common_prefix(pathspec);
+       path = "";
+
+       if (len)
+               path = xmemdupz(*pathspec, len);
+
+       /* Read the directory and prune it */
+       read_directory(dir, path, len, pathspec);
+       return len;
+}
+
 /*
  * Does 'match' match the given name?
  * A match is found if
@@ -307,7 +326,7 @@ static int excluded_1(const char *pathname,
 
                        if (x->flags & EXC_FLAG_MUSTBEDIR) {
                                if (*dtype == DT_UNKNOWN)
-                                       *dtype = get_dtype(NULL, pathname);
+                                       *dtype = get_dtype(NULL, pathname, pathlen);
                                if (*dtype != DT_DIR)
                                        continue;
                        }
@@ -505,7 +524,7 @@ static enum directory_treatment treat_directory(struct dir_struct *dir,
        /* This is the "show_other_directories" case */
        if (!(dir->flags & DIR_HIDE_EMPTY_DIRECTORIES))
                return show_directory;
-       if (!read_directory_recursive(dir, dirname, dirname, len, 1, simplify))
+       if (!read_directory_recursive(dir, dirname, len, 1, simplify))
                return ignore_directory;
        return show_directory;
 }
@@ -547,11 +566,52 @@ static int in_pathspec(const char *path, int len, const struct path_simplify *si
        return 0;
 }
 
-static int get_dtype(struct dirent *de, const char *path)
+static int get_index_dtype(const char *path, int len)
+{
+       int pos;
+       struct cache_entry *ce;
+
+       ce = cache_name_exists(path, len, 0);
+       if (ce) {
+               if (!ce_uptodate(ce))
+                       return DT_UNKNOWN;
+               if (S_ISGITLINK(ce->ce_mode))
+                       return DT_DIR;
+               /*
+                * Nobody actually cares about the
+                * difference between DT_LNK and DT_REG
+                */
+               return DT_REG;
+       }
+
+       /* Try to look it up as a directory */
+       pos = cache_name_pos(path, len);
+       if (pos >= 0)
+               return DT_UNKNOWN;
+       pos = -pos-1;
+       while (pos < active_nr) {
+               ce = active_cache[pos++];
+               if (strncmp(ce->name, path, len))
+                       break;
+               if (ce->name[len] > '/')
+                       break;
+               if (ce->name[len] < '/')
+                       continue;
+               if (!ce_uptodate(ce))
+                       break;  /* continue? */
+               return DT_DIR;
+       }
+       return DT_UNKNOWN;
+}
+
+static int get_dtype(struct dirent *de, const char *path, int len)
 {
        int dtype = de ? DTYPE(de) : DT_UNKNOWN;
        struct stat st;
 
+       if (dtype != DT_UNKNOWN)
+               return dtype;
+       dtype = get_index_dtype(path, len);
        if (dtype != DT_UNKNOWN)
                return dtype;
        if (lstat(path, &st))
@@ -574,15 +634,15 @@ static int get_dtype(struct dirent *de, const char *path)
  * Also, we ignore the name ".git" (even if it is not a directory).
  * That likely will not change.
  */
-static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only, const struct path_simplify *simplify)
+static int read_directory_recursive(struct dir_struct *dir, const char *base, int baselen, int check_only, const struct path_simplify *simplify)
 {
-       DIR *fdir = opendir(*path ? path : ".");
+       DIR *fdir = opendir(*base ? base : ".");
        int contents = 0;
 
        if (fdir) {
                struct dirent *de;
-               char fullname[PATH_MAX + 1];
-               memcpy(fullname, base, baselen);
+               char path[PATH_MAX + 1];
+               memcpy(path, base, baselen);
 
                while ((de = readdir(fdir)) != NULL) {
                        int len, dtype;
@@ -593,17 +653,18 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                                continue;
                        len = strlen(de->d_name);
                        /* Ignore overly long pathnames! */
-                       if (len + baselen + 8 > sizeof(fullname))
+                       if (len + baselen + 8 > sizeof(path))
                                continue;
-                       memcpy(fullname + baselen, de->d_name, len+1);
-                       if (simplify_away(fullname, baselen + len, simplify))
+                       memcpy(path + baselen, de->d_name, len+1);
+                       len = baselen + len;
+                       if (simplify_away(path, len, simplify))
                                continue;
 
                        dtype = DTYPE(de);
-                       exclude = excluded(dir, fullname, &dtype);
+                       exclude = excluded(dir, path, &dtype);
                        if (exclude && (dir->flags & DIR_COLLECT_IGNORED)
-                           && in_pathspec(fullname, baselen + len, simplify))
-                               dir_add_ignored(dir, fullname, baselen + len);
+                           && in_pathspec(path, len, simplify))
+                               dir_add_ignored(dir, path,len);
 
                        /*
                         * Excluded? If we don't explicitly want to show
@@ -613,7 +674,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                                continue;
 
                        if (dtype == DT_UNKNOWN)
-                               dtype = get_dtype(de, fullname);
+                               dtype = get_dtype(de, path, len);
 
                        /*
                         * Do we want to see just the ignored files?
@@ -630,9 +691,9 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                        default:
                                continue;
                        case DT_DIR:
-                               memcpy(fullname + baselen + len, "/", 2);
+                               memcpy(path + len, "/", 2);
                                len++;
-                               switch (treat_directory(dir, fullname, baselen + len, simplify)) {
+                               switch (treat_directory(dir, path, len, simplify)) {
                                case show_directory:
                                        if (exclude != !!(dir->flags
                                                        & DIR_SHOW_IGNORED))
@@ -640,7 +701,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                                        break;
                                case recurse_into_directory:
                                        contents += read_directory_recursive(dir,
-                                               fullname, fullname, baselen + len, 0, simplify);
+                                               path, len, 0, simplify);
                                        continue;
                                case ignore_directory:
                                        continue;
@@ -654,7 +715,7 @@ static int read_directory_recursive(struct dir_struct *dir, const char *path, co
                        if (check_only)
                                goto exit_early;
                        else
-                               dir_add_name(dir, fullname, baselen + len);
+                               dir_add_name(dir, path, len);
                }
 exit_early:
                closedir(fdir);
@@ -717,15 +778,15 @@ static void free_simplify(struct path_simplify *simplify)
        free(simplify);
 }
 
-int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen, const char **pathspec)
+int read_directory(struct dir_struct *dir, const char *path, int len, const char **pathspec)
 {
        struct path_simplify *simplify;
 
-       if (has_symlink_leading_path(path, strlen(path)))
+       if (has_symlink_leading_path(path, len))
                return dir->nr;
 
        simplify = create_simplify(pathspec);
-       read_directory_recursive(dir, path, base, baselen, 0, simplify);
+       read_directory_recursive(dir, path, len, 0, simplify);
        free_simplify(simplify);
        qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
        qsort(dir->ignored, dir->ignored_nr, sizeof(struct dir_entry *), cmp_name);
@@ -759,7 +820,7 @@ char *get_relative_cwd(char *buffer, int size, const char *dir)
        if (!dir)
                return NULL;
        if (!getcwd(buffer, size))
-               die("can't find the current directory: %s", strerror(errno));
+               die_errno("can't find the current directory");
 
        if (!is_absolute_path(dir))
                dir = make_absolute_path(dir);
diff --git a/dir.h b/dir.h
index 541286ad1daeb66a9963288d275534ee963bd5cd..a6314464f9e0d896f131e08434228d5b1130a4f5 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -61,14 +61,13 @@ struct dir_struct {
        char basebuf[PATH_MAX];
 };
 
-extern int common_prefix(const char **pathspec);
-
 #define MATCHED_RECURSIVELY 1
 #define MATCHED_FNMATCH 2
 #define MATCHED_EXACTLY 3
 extern int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen);
 
-extern int read_directory(struct dir_struct *, const char *path, const char *base, int baselen, const char **pathspec);
+extern int fill_directory(struct dir_struct *dir, const char **pathspec);
+extern int read_directory(struct dir_struct *, const char *path, int len, const char **pathspec);
 
 extern int excluded(struct dir_struct *, const char *, int *);
 extern void add_excludes_from_file(struct dir_struct *, const char *fname);
diff --git a/entry.c b/entry.c
index cc841edf9051460b3a382ea25c0097f245ec8884..d3e86c722a6663a65f69b73621f57aa6331a5e5b 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -37,7 +37,7 @@ static void create_directories(const char *path, int path_len,
                        if (errno == EEXIST && state->force &&
                            !unlink_or_warn(buf) && !mkdir(buf, 0777))
                                continue;
-                       die("cannot create directory at %s", buf);
+                       die_errno("cannot create directory at '%s'", buf);
                }
        }
        free(buf);
@@ -51,7 +51,7 @@ static void remove_subtree(const char *path)
        char *name;
 
        if (!dir)
-               die("cannot opendir %s (%s)", path, strerror(errno));
+               die_errno("cannot opendir '%s'", path);
        strcpy(pathbuf, path);
        name = pathbuf + strlen(path);
        *name++ = '/';
@@ -61,15 +61,15 @@ static void remove_subtree(const char *path)
                        continue;
                strcpy(name, de->d_name);
                if (lstat(pathbuf, &st))
-                       die("cannot lstat %s (%s)", pathbuf, strerror(errno));
+                       die_errno("cannot lstat '%s'", pathbuf);
                if (S_ISDIR(st.st_mode))
                        remove_subtree(pathbuf);
                else if (unlink(pathbuf))
-                       die("cannot unlink %s (%s)", pathbuf, strerror(errno));
+                       die_errno("cannot unlink '%s'", pathbuf);
        }
        closedir(dir);
        if (rmdir(path))
-               die("cannot rmdir %s (%s)", path, strerror(errno));
+               die_errno("cannot rmdir '%s'", path);
 }
 
 static int create_file(const char *path, unsigned int mode)
index 801a005ef1b23ef13cfa9ece676c550fe35dedc0..8f5eaa7dd89cbedd5a89e11f0ea24b29701b33c9 100644 (file)
@@ -42,11 +42,12 @@ enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
-enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
+enum push_default_type push_default = PUSH_DEFAULT_MATCHING;
 #ifndef OBJECT_CREATION_MODE
 #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;
index a2a24588a99957d5af759ca6d8858366430ab3a3..7ef9865aa6da794ab52cfc50f21f9f41f861fc4f 100644 (file)
@@ -905,10 +905,10 @@ static char *keep_pack(char *curr_index_name)
 
        keep_fd = odb_pack_keep(name, sizeof(name), pack_data->sha1);
        if (keep_fd < 0)
-               die("cannot create keep file");
+               die_errno("cannot create keep file");
        write_or_die(keep_fd, keep_msg, strlen(keep_msg));
        if (close(keep_fd))
-               die("failed to write keep file");
+               die_errno("failed to write keep file");
 
        snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
                 get_object_directory(), sha1_to_hex(pack_data->sha1));
@@ -2342,7 +2342,7 @@ static void import_marks(const char *input_file)
        char line[512];
        FILE *f = fopen(input_file, "r");
        if (!f)
-               die("cannot read %s: %s", input_file, strerror(errno));
+               die_errno("cannot read '%s'", input_file);
        while (fgets(line, sizeof(line), f)) {
                uintmax_t mark;
                char *end;
@@ -2448,7 +2448,7 @@ int main(int argc, const char **argv)
                                fclose(pack_edges);
                        pack_edges = fopen(a + 20, "a");
                        if (!pack_edges)
-                               die("Cannot open %s: %s", a + 20, strerror(errno));
+                               die_errno("Cannot open '%s'", a + 20);
                } else if (!strcmp(a, "--force"))
                        force_update = 1;
                else if (!strcmp(a, "--quiet"))
diff --git a/fsck.c b/fsck.c
index 511b82cba9977c14d6dcba5efbd6d38591d3aacc..89278c1459d36a3e2b718661ca71483522f587fd 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -229,7 +229,7 @@ static int fsck_commit(struct commit *commit, fsck_error error_func)
        struct commit_graft *graft;
        int parents = 0;
 
-       if (!commit->date)
+       if (commit->date == ULONG_MAX)
                return error_func(&commit->object, FSCK_ERROR, "invalid author/committer line");
 
        if (memcmp(buffer, "tree ", 5))
index 58d4eb6d2dc2eaa99ae0c70a414813cd0ce43e43..d64d9975358a6b9a18596c45b13434463903a344 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -19,6 +19,7 @@ whitespace=     pass it through git-apply
 directory=      pass it through git-apply
 C=              pass it through git-apply
 p=              pass it through git-apply
+patch-format=   format the patch(es) are in
 reject          pass it through git-apply
 resolvemsg=     override error message when patch failure occurs
 r,resolved      to be used after a patch failure
@@ -138,6 +139,126 @@ It does not apply to blobs recorded in its index."
     unset GITHEAD_$his_tree
 }
 
+clean_abort () {
+       test $# = 0 || echo >&2 "$@"
+       rm -fr "$dotest"
+       exit 1
+}
+
+patch_format=
+
+check_patch_format () {
+       # early return if patch_format was set from the command line
+       if test -n "$patch_format"
+       then
+               return 0
+       fi
+
+       # we default to mbox format if input is from stdin and for
+       # directories
+       if test $# = 0 || test "x$1" = "x-" || test -d "$1"
+       then
+               patch_format=mbox
+               return 0
+       fi
+
+       # otherwise, check the first few lines of the first patch to try
+       # to detect its format
+       {
+               read l1
+               read l2
+               read l3
+               case "$l1" in
+               "From "* | "From: "*)
+                       patch_format=mbox
+                       ;;
+               '# This series applies on GIT commit'*)
+                       patch_format=stgit-series
+                       ;;
+               "# HG changeset patch")
+                       patch_format=hg
+                       ;;
+               *)
+                       # if the second line is empty and the third is
+                       # a From, Author or Date entry, this is very
+                       # likely an StGIT patch
+                       case "$l2,$l3" in
+                       ,"From: "* | ,"Author: "* | ,"Date: "*)
+                               patch_format=stgit
+                               ;;
+                       *)
+                               ;;
+                       esac
+                       ;;
+               esac
+       } < "$1" || clean_abort
+}
+
+split_patches () {
+       case "$patch_format" in
+       mbox)
+               git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||
+               clean_abort
+               ;;
+       stgit-series)
+               if test $# -ne 1
+               then
+                       clean_abort "Only one StGIT patch series can be applied at once"
+               fi
+               series_dir=`dirname "$1"`
+               series_file="$1"
+               shift
+               {
+                       set x
+                       while read filename
+                       do
+                               set "$@" "$series_dir/$filename"
+                       done
+                       # remove the safety x
+                       shift
+                       # remove the arg coming from the first-line comment
+                       shift
+               } < "$series_file" || clean_abort
+               # set the patch format appropriately
+               patch_format=stgit
+               # now handle the actual StGIT patches
+               split_patches "$@"
+               ;;
+       stgit)
+               this=0
+               for stgit in "$@"
+               do
+                       this=`expr "$this" + 1`
+                       msgnum=`printf "%0${prec}d" $this`
+                       # Perl version of StGIT parse_patch. The first nonemptyline
+                       # not starting with Author, From or Date is the
+                       # subject, and the body starts with the next nonempty
+                       # line not starting with Author, From or Date
+                       perl -ne 'BEGIN { $subject = 0 }
+                               if ($subject > 1) { print ; }
+                               elsif (/^\s+$/) { next ; }
+                               elsif (/^Author:/) { print s/Author/From/ ; }
+                               elsif (/^(From|Date)/) { print ; }
+                               elsif ($subject) {
+                                       $subject = 2 ;
+                                       print "\n" ;
+                                       print ;
+                               } else {
+                                       print "Subject: ", $_ ;
+                                       $subject = 1;
+                               }
+                       ' < "$stgit" > "$dotest/$msgnum" || clean_abort
+               done
+               echo "$this" > "$dotest/last"
+               this=
+               msgnum=
+               ;;
+       *)
+               clean_abort "Patch format $patch_format is not supported."
+               ;;
+       esac
+}
+
 prec=4
 dotest="$GIT_DIR/rebase-apply"
 sign= utf8=t keep= skip= interactive= resolved= rebasing= abort=
@@ -180,6 +301,8 @@ do
                git_apply_opt="$git_apply_opt $(sq "$1=$2")"; shift ;;
        -C|-p)
                git_apply_opt="$git_apply_opt $(sq "$1$2")"; shift ;;
+       --patch-format)
+               shift ; patch_format="$1" ;;
        --reject)
                git_apply_opt="$git_apply_opt $1" ;;
        --committer-date-is-author-date)
@@ -281,10 +404,10 @@ else
                done
                shift
        fi
-       git mailsplit -d"$prec" -o"$dotest" -b -- "$@" > "$dotest/last" ||  {
-               rm -fr "$dotest"
-               exit 1
-       }
+
+       check_patch_format "$@"
+
+       split_patches "$@"
 
        # -s, -u, -k, --whitespace, -3, -C, -q and -p flags are kept
        # for the resuming session after a patch failure.
index 919b7f1ade9cab3c4ba907f9506e3cb518fb9e7f..9f941e42b16d927f738b4911b41344dd2f3ed0da 100644 (file)
@@ -26,6 +26,7 @@
 #endif
 
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#define bitsizeof(x)  (CHAR_BIT * sizeof(x))
 
 #ifdef __GNUC__
 #define TYPEOF(x) (__typeof__(x))
 #define TYPEOF(x)
 #endif
 
-#define MSB(x, bits) ((x) & TYPEOF(x)(~0ULL << (sizeof(x) * 8 - (bits))))
+#define MSB(x, bits) ((x) & TYPEOF(x)(~0ULL << (bitsizeof(x) - (bits))))
 #define HAS_MULTI_BITS(i)  ((i) & ((i) - 1))  /* checks if an integer has more than 1 bit set */
 
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+
 /* Approximation of the length of the decimal representation of this type. */
 #define decimal_length(x)      ((int)(sizeof(x) * 2.56 + 0.5) + 1)
 
@@ -52,7 +55,7 @@
 # else
 # define _XOPEN_SOURCE 500
 # endif
-#elif !defined(__APPLE__) && !defined(__FreeBSD__)  && !defined(__USLC__) && !defined(_M_UNIX)
+#elif !defined(__APPLE__) && !defined(__FreeBSD__)  && !defined(__USLC__) && !defined(_M_UNIX) && !defined(sgi)
 #define _XOPEN_SOURCE 600 /* glibc2 and AIX 5.3L need 500, OpenBSD needs 600 for S_ISLNK() */
 #ifndef __sun__
 #define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
@@ -62,6 +65,7 @@
 #define _GNU_SOURCE 1
 #define _BSD_SOURCE 1
 #define _NETBSD_SOURCE 1
+#define _SGI_SOURCE 1
 
 #include <unistd.h>
 #include <stdio.h>
@@ -175,6 +179,7 @@ extern char *gitbasename(char *);
 /* General helper functions */
 extern void usage(const char *err) NORETURN;
 extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
+extern void die_errno(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
 extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
 extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
 
index a36df3392652b8a854ca48bdf71977dcf12b244b..59b672213bfc36f95db089f0e13bafc1c2f2ed71 100755 (executable)
@@ -259,7 +259,8 @@ if (@canstatusfiles) {
            if $file =~ /^no file /
                && $status eq 'Up-to-date';
 
-       $cvsstat{$fullname{$file}} = $status;
+       $cvsstat{$fullname{$file}} = $status
+           if defined $fullname{$file};
       }
     }
 }
@@ -299,7 +300,7 @@ foreach my $f (@files) {
        while (<FILTER_IN>)
        {
            my $line = $_;
-           $line =~ s/\$([A-Z][a-z]+):[^\$]+\$/\$\1\$/g;
+           $line =~ s/\$([A-Z][a-z]+):[^\$]+\$/\$$1\$/g;
            print FILTER_OUT $line;
        }
        close FILTER_IN;
index 5f4419b69b58769e8fee7a60109520a095a831c5..32f6496b0d36cff061f00a4ad70b6fa4d34c2646 100755 (executable)
@@ -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,7 +272,7 @@ EOF
                fi
        done
        cat >> "$conf" <<EOF
-TypesConfig $fqgitdir/mime.types
+TypesConfig "$fqgitdir/mime.types"
 DirectoryIndex gitweb.cgi
 EOF
 
index 4b78a0cd37ba70236a05f78fb5bed3f763348096..0f24182974fe2040c950b846decf360c1c22dbb1 100755 (executable)
@@ -124,10 +124,18 @@ test true = "$rebase" && {
        git diff-index --ignore-submodules --cached --quiet HEAD -- ||
        die "refusing to pull with rebase: your working tree is not up-to-date"
 
+       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
index f96d887d23653019e3387eced2779d50b3f09fa2..23ded48322bf795b06aa40e4684bc710c8232549 100755 (executable)
@@ -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
index 18bc6946cfa592c11cf7499b1c1ff9a6cb378b26..3555d17a5d0f4f6e79cd2283efec04d89668017c 100755 (executable)
@@ -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
index 1bf239499c7bdb04622ea6fa239bd7c0ace42065..1eb3bca352f38ec3807383cc5fff7f7e62731b2b 100755 (executable)
@@ -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.
index 5917773240dfb2e8b5f078afee22a3d9bf607ce3..630ceddf0356429f7ff71d280ee1056a2eb939c6 100755 (executable)
@@ -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
index 8ce6f1fe57579201c09cf960caed98d330ab2bbb..0700d80afcf99dfafbd34731bf3c359b7d921ff3 100755 (executable)
@@ -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);
index 531c7c31aca87db77e54c46ffb89899652a79fcd..03e589f764ba0570559fdb23a9a11cc2a0f24044 100755 (executable)
@@ -203,7 +203,7 @@ apply_stash () {
                git diff-tree --binary $s^2^..$s^2 | git apply --cached
                test $? -ne 0 &&
                        die 'Conflicts in index. Try without --index.'
-               unstashed_index_tree=$(git-write-tree) ||
+               unstashed_index_tree=$(git write-tree) ||
                        die 'Could not save index tree'
                git reset
        fi
@@ -219,7 +219,7 @@ apply_stash () {
        then
                export GIT_MERGE_VERBOSITY=0
        fi
-       if git-merge-recursive $b_tree -- $c_tree $w_tree
+       if git merge-recursive $b_tree -- $c_tree $w_tree
        then
                # No conflict
                if test -n "$unstashed_index_tree"
@@ -297,7 +297,7 @@ apply_to_branch () {
        fi
        stash=$2
 
-       git-checkout -b $branch $stash^ &&
+       git checkout -b $branch $stash^ &&
        apply_stash --index $stash &&
        drop_stash $stash
 }
index d1af1a3d2f1e24c068e227f847ccceb7e156ad0d..b0bfb74792d775155221bc857bb117479d3cbca2 100755 (executable)
@@ -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.
@@ -876,10 +902,6 @@ sub cmd_multi_init {
                usage(1);
        }
 
-       # there are currently some bugs that prevent multi-init/multi-fetch
-       # setups from working well without this.
-       $Git::SVN::_minimize_url = 1;
-
        $_prefix = '' unless defined $_prefix;
        if (defined $url) {
                $url = canonicalize_url($url);
@@ -1111,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 {
@@ -1180,7 +1210,7 @@ sub complete_url_ls_init {
                    "wanted to set to: $gs->{url}\n";
        }
        command_oneline('config', $k, $gs->{url}) unless $orig_url;
-       my $remote_path = "$ra->{svn_path}/$repo_path";
+       my $remote_path = "$gs->{path}/$repo_path";
        $remote_path =~ s#/+#/#g;
        $remote_path =~ s#^/##g;
        $remote_path .= "/*" if $remote_path !~ /\*/;
@@ -1363,11 +1393,11 @@ sub read_repo_config {
 sub extract_metadata {
        my $id = shift or return (undef, undef, undef);
        my ($url, $rev, $uuid) = ($id =~ /^\s*git-svn-id:\s+(.*)\@(\d+)
-                                                       \s([a-f\d\-]+)$/x);
+                                                       \s([a-f\d\-]+)$/ix);
        if (!defined $rev || !$uuid || !$url) {
                # some of the original repositories I made had
                # identifiers like this:
-               ($rev, $uuid) = ($id =~/^\s*git-svn-id:\s(\d+)\@([a-f\d\-]+)/);
+               ($rev, $uuid) = ($id =~/^\s*git-svn-id:\s(\d+)\@([a-f\d\-]+)/i);
        }
        return ($url, $rev, $uuid);
 }
@@ -1531,6 +1561,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;
@@ -1651,6 +1700,7 @@ sub fetch_all {
        my $ra = Git::SVN::Ra->new($url);
        my $uuid = $ra->get_uuid;
        my $head = $ra->get_latest_revnum;
+       $ra->get_log("", $head, 0, 1, 0, 1, sub { $head = $_[1] });
        my $base = defined $fetch ? $head : 0;
 
        # read the max revs for wildcard expansion (branches/*, tags/*)
@@ -2014,7 +2064,7 @@ sub _set_svm_vars {
 
                chomp($src, $uuid);
 
-               $uuid =~ m{^[0-9a-f\-]{30,}$}
+               $uuid =~ m{^[0-9a-f\-]{30,}$}i
                    or die "doesn't look right - svm:uuid is '$uuid'\n";
 
                # the '!' is used to mark the repos_root!/relative/path
@@ -2100,7 +2150,7 @@ sub svnsync {
                   die "doesn't look right - svn:sync-from-url is '$url'\n";
 
                my $uuid = tmp_config('--get', "$section.svnsync-uuid");
-               ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}) or
+               ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}i) or
                   die "doesn't look right - svn:sync-from-uuid is '$uuid'\n";
 
                $svnsync = { url => $url, uuid => $uuid }
@@ -2118,7 +2168,7 @@ sub svnsync {
                   die "doesn't look right - svn:sync-from-url is '$url'\n";
 
        my $uuid = $rp->{'svn:sync-from-uuid'} or die $err . "uuid\n";
-       ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}) or
+       ($uuid) = ($uuid =~ m{^([0-9a-f\-]{30,})$}i) or
                   die "doesn't look right - svn:sync-from-uuid is '$uuid'\n";
 
        my $section = "svn-remote.$self->{repo_id}";
@@ -2134,7 +2184,7 @@ sub ra_uuid {
        unless ($self->{ra_uuid}) {
                my $key = "svn-remote.$self->{repo_id}.uuid";
                my $uuid = eval { tmp_config('--get', $key) };
-               if (!$@ && $uuid && $uuid =~ /^([a-f\d\-]{30,})$/) {
+               if (!$@ && $uuid && $uuid =~ /^([a-f\d\-]{30,})$/i) {
                        $self->{ra_uuid} = $uuid;
                } else {
                        die "ra_uuid called without URL\n" unless $self->{url};
@@ -2177,16 +2227,6 @@ sub ra {
        $ra;
 }
 
-sub rel_path {
-       my ($self) = @_;
-       my $repos_root = $self->ra->{repos_root};
-       return $self->{path} if ($self->{url} eq $repos_root);
-       my $url = $self->{url} .
-                 (length $self->{path} ? "/$self->{path}" : $self->{path});
-       $url =~ s!^\Q$repos_root\E(?:/+|$)!!g;
-       $url;
-}
-
 # prop_walk(PATH, REV, SUB)
 # -------------------------
 # Recursively traverse PATH at revision REV and invoke SUB for each
@@ -2512,10 +2552,7 @@ sub match_paths {
        if (my $path = $paths->{"/$self->{path}"}) {
                return ($path->{action} eq 'D') ? 0 : 1;
        }
-       my $repos_root = $self->ra->{repos_root};
-       my $extended_path = $self->{url} . '/' . $self->{path};
-       $extended_path =~ s#^\Q$repos_root\E(/|$)##;
-       $self->{path_regex} ||= qr/^\/\Q$extended_path\E\//;
+       $self->{path_regex} ||= qr/^\/\Q$self->{path}\E\//;
        if (grep /$self->{path_regex}/, keys %$paths) {
                return 1;
        }
@@ -2538,15 +2575,14 @@ sub find_parent_branch {
        unless (defined $paths) {
                my $err_handler = $SVN::Error::handler;
                $SVN::Error::handler = \&Git::SVN::Ra::skip_unknown_revs;
-               $self->ra->get_log([$self->{path}], $rev, $rev, 0, 1, 1, sub {
-                                  $paths =
-                                     Git::SVN::Ra::dup_changed_paths($_[0]) });
+               $self->ra->get_log([$self->{path}], $rev, $rev, 0, 1, 1,
+                                  sub { $paths = $_[0] });
                $SVN::Error::handler = $err_handler;
        }
        return undef unless defined $paths;
 
        # look for a parent from another branch:
-       my @b_path_components = split m#/#, $self->rel_path;
+       my @b_path_components = split m#/#, $self->{path};
        my @a_path_components;
        my $i;
        while (@b_path_components) {
@@ -2564,11 +2600,11 @@ sub find_parent_branch {
        my $r = $i->{copyfrom_rev};
        my $repos_root = $self->ra->{repos_root};
        my $url = $self->ra->{url};
-       my $new_url = $repos_root . $branch_from;
+       my $new_url = $url . $branch_from;
        print STDERR  "Found possible branch point: ",
                      "$new_url => ", $self->full_url, ", $r\n";
        $branch_from =~ s#^/##;
-       my $gs = $self->other_gs($new_url, $url, $repos_root,
+       my $gs = $self->other_gs($new_url, $url,
                                 $branch_from, $r, $self->{ref_id});
        my ($r0, $parent) = $gs->find_rev_before($r, 1);
        {
@@ -2753,9 +2789,9 @@ sub parse_svn_date {
 }
 
 sub other_gs {
-       my ($self, $new_url, $url, $repos_root,
+       my ($self, $new_url, $url,
            $branch_from, $r, $old_ref_id) = @_;
-       my $gs = Git::SVN->find_by_url($new_url, $repos_root, $branch_from);
+       my $gs = Git::SVN->find_by_url($new_url, $url, $branch_from);
        unless ($gs) {
                my $ref_id = $old_ref_id;
                $ref_id =~ s/\@\d+$//;
@@ -2866,7 +2902,7 @@ sub make_log_entry {
                        die "Can't have both 'useSvmProps' and 'rewriteRoot' ",
                            "options set!\n";
                }
-               my ($uuid, $r) = $headrev =~ m{^([a-f\d\-]{30,}):(\d+)$};
+               my ($uuid, $r) = $headrev =~ m{^([a-f\d\-]{30,}):(\d+)$}i;
                # we don't want "SVM: initializing mirror for junk" ...
                return undef if $r == 0;
                my $svm = $self->svm;
@@ -3283,7 +3319,8 @@ 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 = $_prefix . $Git::SVN::default_ref_id;
        }
        $_[1] = $repo_id;
        my $dir = "$ENV{GIT_DIR}/svn/$ref_id";
@@ -3971,7 +4008,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);
 }
@@ -4431,6 +4468,34 @@ sub get_log {
        my ($self, @args) = @_;
        my $pool = SVN::Pool->new;
 
+       # svn_log_changed_path_t objects passed to get_log are likely to be
+       # overwritten even if only the refs are copied to an external variable,
+       # so we should dup the structures in their entirety.  Using an
+       # externally passed pool (instead of our temporary and quickly cleared
+       # pool in Git::SVN::Ra) does not help matters at all...
+       my $receiver = pop @args;
+       my $prefix = "/".$self->{svn_path};
+       $prefix =~ s#/+($)##;
+       my $prefix_regex = qr#^\Q$prefix\E#;
+       push(@args, sub {
+               my ($paths) = $_[0];
+               return &$receiver(@_) unless $paths;
+               $_[0] = ();
+               foreach my $p (keys %$paths) {
+                       my $i = $paths->{$p};
+                       # Make path relative to our url, not repos_root
+                       $p =~ s/$prefix_regex//;
+                       my %s = map { $_ => $i->$_; }
+                               qw/copyfrom_path copyfrom_rev action/;
+                       if ($s{'copyfrom_path'}) {
+                               $s{'copyfrom_path'} =~ s/$prefix_regex//;
+                       }
+                       $_[0]{$p} = \%s;
+               }
+               &$receiver(@_);
+       });
+
+
        # the limit parameter was not supported in SVN 1.1.x, so we
        # drop it.  Therefore, the receiver callback passed to it
        # is made aware of this limitation by being wrapped if
@@ -4515,10 +4580,12 @@ sub gs_do_switch {
 
        my $full_url = $self->{url};
        my $old_url = $full_url;
-       $full_url .= '/' . escape_uri_only($path) if length $path;
+       $full_url .= '/' . $path if length $path;
        my ($ra, $reparented);
 
-       if ($old_url =~ m#^svn(\+ssh)?://#) {
+       if ($old_url =~ m#^svn(\+ssh)?://# ||
+           ($full_url =~ m#^https?://# &&
+            escape_url($full_url) ne $full_url)) {
                $_[0] = undef;
                $self = undef;
                $RA = undef;
@@ -4600,7 +4667,7 @@ sub gs_fetch_loop_common {
                };
                sub _cb {
                        my ($paths, $r, $author, $date, $log) = @_;
-                       [ dup_changed_paths($paths),
+                       [ $paths,
                          { author => $author, date => $date, log => $log } ];
                }
                $self->get_log([$longest_path], $min, $max, 0, 1, 1,
@@ -4767,7 +4834,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;
 }
@@ -4823,24 +4894,6 @@ sub skip_unknown_revs {
        die "Error from SVN, ($errno): ", $err->expanded_message,"\n";
 }
 
-# svn_log_changed_path_t objects passed to get_log are likely to be
-# overwritten even if only the refs are copied to an external variable,
-# so we should dup the structures in their entirety.  Using an externally
-# passed pool (instead of our temporary and quickly cleared pool in
-# Git::SVN::Ra) does not help matters at all...
-sub dup_changed_paths {
-       my ($paths) = @_;
-       return undef unless $paths;
-       my %ret;
-       foreach my $p (keys %$paths) {
-               my $i = $paths->{$p};
-               my %s = map { $_ => $i->$_ }
-                             qw/copyfrom_path copyfrom_rev action/;
-               $ret{$p} = \%s;
-       }
-       \%ret;
-}
-
 package Git::SVN::Log;
 use strict;
 use warnings;
diff --git a/git.c b/git.c
index 18240280e8383a73e575ff0fc519dcfa6bf1db42..4588a8bac21a12240c84eac4b24d68cb72920d80 100644 (file)
--- a/git.c
+++ b/git.c
@@ -199,7 +199,7 @@ static int handle_alias(int *argcp, const char ***argv)
        }
 
        if (subdir && chdir(subdir))
-               die("Cannot change to %s: %s", subdir, strerror(errno));
+               die_errno("Cannot change to '%s'", subdir);
 
        errno = saved_errno;
 
@@ -256,11 +256,11 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
 
        /* Check for ENOSPC and EIO errors.. */
        if (fflush(stdout))
-               die("write failure on standard output: %s", strerror(errno));
+               die_errno("write failure on standard output");
        if (ferror(stdout))
                die("unknown write failure on standard output");
        if (fclose(stdout))
-               die("close failed on standard output: %s", strerror(errno));
+               die_errno("close failed on standard output");
        return 0;
 }
 
index 9056d1e090b314cfa617ccd8aa03047b3aea23b4..66c6a9391d3494ae8928e88501d8fa0d36dffa42 100644 (file)
@@ -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).
index a01eac814e51edd2115474f4d48a8d7fafa8c0e6..d05bc376463c3412f50f299949fb836aee5c12da 100644 (file)
@@ -28,6 +28,10 @@ img.logo {
        border-width: 0px;
 }
 
+img.avatar {
+       vertical-align: middle;
+}
+
 div.page_header {
        height: 25px;
        padding: 8px;
@@ -132,11 +136,14 @@ div.list_head {
        font-style: italic;
 }
 
+.author_date, .author {
+       font-style: italic;
+}
+
 div.author_date {
        padding: 8px;
        border: solid #d9d8d1;
        border-width: 0px 0px 1px 0px;
-       font-style: italic;
 }
 
 a.list {
index 1e7e2d8387efb810afb3b9c9d279cc59f52d932e..37120a3e606a2e3e192cf59e2e69d29efd774d33 100755 (executable)
@@ -94,7 +94,7 @@ our $favicon = "++GITWEB_FAVICON++";
 # URI and label (title) of GIT logo link
 #our $logo_url = "http://www.kernel.org/pub/software/scm/git/docs/";
 #our $logo_label = "git documentation";
-our $logo_url = "http://git.or.cz/";
+our $logo_url = "http://git-scm.com/";
 our $logo_label = "git homepage";
 
 # source of projects list
@@ -195,6 +195,14 @@ our %known_snapshot_format_aliases = (
        'x-zip' => undef, '' => undef,
 );
 
+# Pixel sizes for icons and avatars. If the default font sizes or lineheights
+# are changed, it may be appropriate to change these values too via
+# $GITWEB_CONFIG.
+our %avatar_size = (
+       'default' => 16,
+       'double'  => 32
+);
+
 # You define site-wide feature defaults here; override them with
 # $GITWEB_CONFIG as necessary.
 our %feature = (
@@ -365,6 +373,27 @@ our %feature = (
                'sub' => \&feature_patches,
                'override' => 0,
                'default' => [16]},
+
+       # Avatar support. When this feature is enabled, views such as
+       # shortlog or commit will display an avatar associated with
+       # the email of the committer(s) and/or author(s).
+
+       # Currently available providers are gravatar and picon.
+       # If an unknown provider is specified, the feature is disabled.
+
+       # Gravatar depends on Digest::MD5.
+       # Picon currently relies on the indiana.edu database.
+
+       # To enable system wide have in $GITWEB_CONFIG
+       # $feature{'avatar'}{'default'} = ['<provider>'];
+       # where <provider> is either gravatar or picon.
+       # To have project specific config enable override in $GITWEB_CONFIG
+       # $feature{'avatar'}{'override'} = 1;
+       # and in project config gitweb.avatar = <provider>;
+       'avatar' => {
+               'sub' => \&feature_avatar,
+               'override' => 0,
+               'default' => ['']},
 );
 
 sub gitweb_get_feature {
@@ -433,6 +462,12 @@ sub feature_patches {
        return ($_[0]);
 }
 
+sub feature_avatar {
+       my @val = (git_get_project_config('avatar'));
+
+       return @val ? @val : @_;
+}
+
 # checking HEAD file with -e is fragile if the repository was
 # initialized long time ago (i.e. symlink HEAD) and was pack-ref'ed
 # and then pruned.
@@ -814,6 +849,19 @@ $git_dir = "$projectroot/$project" if $project;
 our @snapshot_fmts = gitweb_get_feature('snapshot');
 @snapshot_fmts = filter_snapshot_fmts(@snapshot_fmts);
 
+# check that the avatar feature is set to a known provider name,
+# and for each provider check if the dependencies are satisfied.
+# if the provider name is invalid or the dependencies are not met,
+# reset $git_avatar to the empty string.
+our ($git_avatar) = gitweb_get_feature('avatar');
+if ($git_avatar eq 'gravatar') {
+       $git_avatar = '' unless (eval { require Digest::MD5; 1; });
+} elsif ($git_avatar eq 'picon') {
+       # no dependencies
+} else {
+       $git_avatar = '';
+}
+
 # dispatch
 if (!defined $action) {
        if (defined $hash) {
@@ -892,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'};
@@ -1469,6 +1520,82 @@ sub format_subject_html {
        }
 }
 
+# Rather than recomputing the url for an email multiple times, we cache it
+# after the first hit. This gives a visible benefit in views where the avatar
+# for the same email is used repeatedly (e.g. shortlog).
+# The cache is shared by all avatar engines (currently gravatar only), which
+# are free to use it as preferred. Since only one avatar engine is used for any
+# given page, there's no risk for cache conflicts.
+our %avatar_cache = ();
+
+# Compute the picon url for a given email, by using the picon search service over at
+# http://www.cs.indiana.edu/picons/search.html
+sub picon_url {
+       my $email = lc shift;
+       if (!$avatar_cache{$email}) {
+               my ($user, $domain) = split('@', $email);
+               $avatar_cache{$email} =
+                       "http://www.cs.indiana.edu/cgi-pub/kinzler/piconsearch.cgi/" .
+                       "$domain/$user/" .
+                       "users+domains+unknown/up/single";
+       }
+       return $avatar_cache{$email};
+}
+
+# Compute the gravatar url for a given email, if it's not in the cache already.
+# Gravatar stores only the part of the URL before the size, since that's the
+# one computationally more expensive. This also allows reuse of the cache for
+# different sizes (for this particular engine).
+sub gravatar_url {
+       my $email = lc shift;
+       my $size = shift;
+       $avatar_cache{$email} ||=
+               "http://www.gravatar.com/avatar/" .
+                       Digest::MD5::md5_hex($email) . "?s=";
+       return $avatar_cache{$email} . $size;
+}
+
+# Insert an avatar for the given $email at the given $size if the feature
+# is enabled.
+sub git_get_avatar {
+       my ($email, %opts) = @_;
+       my $pre_white  = ($opts{-pad_before} ? "&nbsp;" : "");
+       my $post_white = ($opts{-pad_after}  ? "&nbsp;" : "");
+       $opts{-size} ||= 'default';
+       my $size = $avatar_size{$opts{-size}} || $avatar_size{'default'};
+       my $url = "";
+       if ($git_avatar eq 'gravatar') {
+               $url = gravatar_url($email, $size);
+       } elsif ($git_avatar eq 'picon') {
+               $url = picon_url($email);
+       }
+       # Other providers can be added by extending the if chain, defining $url
+       # as needed. If no variant puts something in $url, we assume avatars
+       # are completely disabled/unavailable.
+       if ($url) {
+               return $pre_white .
+                      "<img width=\"$size\" " .
+                           "class=\"avatar\" " .
+                           "src=\"$url\" " .
+                           "alt=\"\" " .
+                      "/>" . $post_white;
+       } else {
+               return "";
+       }
+}
+
+# format the author name of the given commit with the given tag
+# the author name is chopped and escaped according to the other
+# optional parameters (see chop_str).
+sub format_author_html {
+       my $tag = shift;
+       my $co = shift;
+       my $author = chop_and_escape_str($co->{'author_name'}, @_);
+       return "<$tag class=\"author\">" .
+              git_get_avatar($co->{'author_email'}, -pad_after => 1) .
+              $author . "</$tag>";
+}
+
 # format git diff header line, i.e. "diff --(git|combined|cc) ..."
 sub format_git_diff_header_line {
        my $line = shift;
@@ -2399,8 +2526,14 @@ sub parse_tag {
                        $tag{'name'} = $1;
                } elsif ($line =~ m/^tagger (.*) ([0-9]+) (.*)$/) {
                        $tag{'author'} = $1;
-                       $tag{'epoch'} = $2;
-                       $tag{'tz'} = $3;
+                       $tag{'author_epoch'} = $2;
+                       $tag{'author_tz'} = $3;
+                       if ($tag{'author'} =~ m/^([^<]+) <([^>]*)>/) {
+                               $tag{'author_name'}  = $1;
+                               $tag{'author_email'} = $2;
+                       } else {
+                               $tag{'author_name'} = $tag{'author'};
+                       }
                } elsif ($line =~ m/--BEGIN/) {
                        push @comment, $line;
                        last;
@@ -3214,21 +3347,54 @@ sub git_print_header_div {
              "\n</div>\n";
 }
 
+sub print_local_time {
+       my %date = @_;
+       if ($date{'hour_local'} < 6) {
+               printf(" (<span class=\"atnight\">%02d:%02d</span> %s)",
+                       $date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});
+       } else {
+               printf(" (%02d:%02d %s)",
+                       $date{'hour_local'}, $date{'minute_local'}, $date{'tz_local'});
+       }
+}
+
+# Outputs the author name and date in long form
 sub git_print_authorship {
        my $co = shift;
+       my %opts = @_;
+       my $tag = $opts{-tag} || 'div';
 
        my %ad = parse_date($co->{'author_epoch'}, $co->{'author_tz'});
-       print "<div class=\"author_date\">" .
+       print "<$tag class=\"author_date\">" .
              esc_html($co->{'author_name'}) .
              " [$ad{'rfc2822'}";
-       if ($ad{'hour_local'} < 6) {
-               printf(" (<span class=\"atnight\">%02d:%02d</span> %s)",
-                      $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
-       } else {
-               printf(" (%02d:%02d %s)",
-                      $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
+       print_local_time(%ad) if ($opts{-localtime});
+       print "]" . git_get_avatar($co->{'author_email'}, -pad_before => 1)
+                 . "</$tag>\n";
+}
+
+# Outputs table rows containing the full author or committer information,
+# in the format expected for 'commit' view (& similia).
+# Parameters are a commit hash reference, followed by the list of people
+# to output information for. If the list is empty it defalts to both
+# author and committer.
+sub git_print_authorship_rows {
+       my $co = shift;
+       # too bad we can't use @people = @_ || ('author', 'committer')
+       my @people = @_;
+       @people = ('author', 'committer') unless @people;
+       foreach my $who (@people) {
+               my %wd = parse_date($co->{"${who}_epoch"}, $co->{"${who}_tz"});
+               print "<tr><td>$who</td><td>" . esc_html($co->{$who}) . "</td>" .
+                     "<td rowspan=\"2\">" .
+                     git_get_avatar($co->{"${who}_email"}, -size => 'double') .
+                     "</td></tr>\n" .
+                     "<tr>" .
+                     "<td></td><td> $wd{'rfc2822'}";
+               print_local_time(%wd);
+               print "</td>" .
+                     "</tr>\n";
        }
-       print "]</div>\n";
 }
 
 sub git_print_page_path {
@@ -4142,11 +4308,9 @@ sub git_shortlog_body {
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
-               my $author = chop_and_escape_str($co{'author_name'}, 10);
                # git_summary() used print "<td><i>$co{'age_string'}</i></td>\n" .
                print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
-                     "<td><i>" . $author . "</i></td>\n" .
-                     "<td>";
+                     format_author_html('td', \%co, 10) . "<td>";
                print format_subject_html($co{'title'}, $co{'title_short'},
                                          href(action=>"commit", hash=>$commit), $ref);
                print "</td>\n" .
@@ -4193,11 +4357,9 @@ sub git_history_body {
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
-       # shortlog uses      chop_str($co{'author_name'}, 10)
-               my $author = chop_and_escape_str($co{'author_name'}, 15, 3);
                print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
-                     "<td><i>" . $author . "</i></td>\n" .
-                     "<td>";
+       # shortlog:   format_author_html('td', \%co, 10)
+                     format_author_html('td', \%co, 15, 3) . "<td>";
                # originally git_history used chop_str($co{'title'}, 50)
                print format_subject_html($co{'title'}, $co{'title_short'},
                                          href(action=>"commit", hash=>$commit), $ref);
@@ -4350,9 +4512,8 @@ sub git_search_grep_body {
                        print "<tr class=\"light\">\n";
                }
                $alternate ^= 1;
-               my $author = chop_and_escape_str($co{'author_name'}, 15, 5);
                print "<td title=\"$co{'age_string_age'}\"><i>$co{'age_string_date'}</i></td>\n" .
-                     "<td><i>" . $author . "</i></td>\n" .
+                     format_author_html('td', \%co, 15, 5) .
                      "<td>" .
                      $cgi->a({-href => href(action=>"commit", hash=>$co{'id'}),
                               -class => "list subject"},
@@ -4586,11 +4747,7 @@ sub git_tag {
                                              $tag{'type'}) . "</td>\n" .
              "</tr>\n";
        if (defined($tag{'author'})) {
-               my %ad = parse_date($tag{'epoch'}, $tag{'tz'});
-               print "<tr><td>author</td><td>" . esc_html($tag{'author'}) . "</td></tr>\n";
-               print "<tr><td></td><td>" . $ad{'rfc2822'} .
-                       sprintf(" (%02d:%02d %s)", $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'}) .
-                       "</td></tr>\n";
+               git_print_authorship_rows(\%tag, 'author');
        }
        print "</table>\n\n" .
              "</div>\n";
@@ -5094,9 +5251,9 @@ sub git_log {
                      " | " .
                      $cgi->a({-href => href(action=>"tree", hash=>$commit, hash_base=>$commit)}, "tree") .
                      "<br/>\n" .
-                     "</div>\n" .
-                     "<i>" . esc_html($co{'author_name'}) .  " [$ad{'rfc2822'}]</i><br/>\n" .
                      "</div>\n";
+                     git_print_authorship(\%co, -tag => 'span');
+                     print "<br/>\n</div>\n";
 
                print "<div class=\"log_body\">\n";
                git_print_log($co{'comment'}, -final_empty_line=> 1);
@@ -5115,8 +5272,6 @@ sub git_commit {
        $hash ||= $hash_base || "HEAD";
        my %co = parse_commit($hash)
            or die_error(404, "Unknown commit object");
-       my %ad = parse_date($co{'author_epoch'}, $co{'author_tz'});
-       my %cd = parse_date($co{'committer_epoch'}, $co{'committer_tz'});
 
        my $parent  = $co{'parent'};
        my $parents = $co{'parents'}; # listref
@@ -5183,22 +5338,7 @@ sub git_commit {
        }
        print "<div class=\"title_text\">\n" .
              "<table class=\"object_header\">\n";
-       print "<tr><td>author</td><td>" . esc_html($co{'author'}) . "</td></tr>\n".
-             "<tr>" .
-             "<td></td><td> $ad{'rfc2822'}";
-       if ($ad{'hour_local'} < 6) {
-               printf(" (<span class=\"atnight\">%02d:%02d</span> %s)",
-                      $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
-       } else {
-               printf(" (%02d:%02d %s)",
-                      $ad{'hour_local'}, $ad{'minute_local'}, $ad{'tz_local'});
-       }
-       print "</td>" .
-             "</tr>\n";
-       print "<tr><td>committer</td><td>" . esc_html($co{'committer'}) . "</td></tr>\n";
-       print "<tr><td></td><td> $cd{'rfc2822'}" .
-             sprintf(" (%02d:%02d %s)", $cd{'hour_local'}, $cd{'minute_local'}, $cd{'tz_local'}) .
-             "</td></tr>\n";
+       git_print_authorship_rows(\%co);
        print "<tr><td>commit</td><td class=\"sha1\">$co{'id'}</td></tr>\n";
        print "<tr>" .
              "<td>tree</td>" .
@@ -5579,7 +5719,11 @@ sub git_commitdiff {
                git_header_html(undef, $expires);
                git_print_page_nav('commitdiff','', $hash,$co{'tree'},$hash, $formats_nav);
                git_print_header_div('commit', esc_html($co{'title'}) . $ref, $hash);
-               git_print_authorship(\%co);
+               print "<div class=\"title_text\">\n" .
+                     "<table class=\"object_header\">\n";
+               git_print_authorship_rows(\%co);
+               print "</table>".
+                     "</div>\n";
                print "<div class=\"page_body\">\n";
                if (@{$co{'comment'}} > 1) {
                        print "<div class=\"log\">\n";
diff --git a/graph.c b/graph.c
index f8d7a5c0f6d17538a72b75e8de9dd092829a0262..e466770208d73d8e1f5bc7f6b12728b6f2b7c6bb 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -893,7 +893,7 @@ static struct column *find_new_column_by_commit(struct git_graph *graph,
                if (graph->new_columns[i].commit == commit)
                        return &graph->new_columns[i];
        }
-       return 0;
+       return NULL;
 }
 
 static void graph_output_post_merge_line(struct git_graph *graph, struct strbuf *sb)
diff --git a/grep.c b/grep.c
index 92a47c71e7d93eef7dc8d6967cd071aa061218ce..5d162dae6e43cdfcf6b20627df088a73cbfa98dc 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "grep.h"
+#include "userdiff.h"
 #include "xdiff-interface.h"
 
 void append_header_grep_pattern(struct grep_opt *opt, enum grep_header_field field, const char *pat)
@@ -490,6 +491,17 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
 {
        int rest = eol - bol;
 
+       if (opt->pre_context || opt->post_context) {
+               if (opt->last_shown == 0) {
+                       if (opt->show_hunk_mark)
+                               fputs("--\n", stdout);
+                       else
+                               opt->show_hunk_mark = 1;
+               } else if (lno > opt->last_shown + 1)
+                       fputs("--\n", stdout);
+       }
+       opt->last_shown = lno;
+
        if (opt->null_following_name)
                sign = '\0';
        if (opt->pathname)
@@ -520,22 +532,95 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
        printf("%.*s\n", rest, bol);
 }
 
+static int match_funcname(struct grep_opt *opt, char *bol, char *eol)
+{
+       xdemitconf_t *xecfg = opt->priv;
+       if (xecfg && xecfg->find_func) {
+               char buf[1];
+               return xecfg->find_func(bol, eol - bol, buf, 1,
+                                       xecfg->find_func_priv) >= 0;
+       }
+
+       if (bol == eol)
+               return 0;
+       if (isalpha(*bol) || *bol == '_' || *bol == '$')
+               return 1;
+       return 0;
+}
+
+static void show_funcname_line(struct grep_opt *opt, const char *name,
+                              char *buf, char *bol, unsigned lno)
+{
+       while (bol > buf) {
+               char *eol = --bol;
+
+               while (bol > buf && bol[-1] != '\n')
+                       bol--;
+               lno--;
+
+               if (lno <= opt->last_shown)
+                       break;
+
+               if (match_funcname(opt, bol, eol)) {
+                       show_line(opt, bol, eol, name, lno, '=');
+                       break;
+               }
+       }
+}
+
+static void show_pre_context(struct grep_opt *opt, const char *name, char *buf,
+                            char *bol, unsigned lno)
+{
+       unsigned cur = lno, from = 1, funcname_lno = 0;
+       int funcname_needed = opt->funcname;
+
+       if (opt->pre_context < lno)
+               from = lno - opt->pre_context;
+       if (from <= opt->last_shown)
+               from = opt->last_shown + 1;
+
+       /* Rewind. */
+       while (bol > buf && cur > from) {
+               char *eol = --bol;
+
+               while (bol > buf && bol[-1] != '\n')
+                       bol--;
+               cur--;
+               if (funcname_needed && match_funcname(opt, bol, eol)) {
+                       funcname_lno = cur;
+                       funcname_needed = 0;
+               }
+       }
+
+       /* We need to look even further back to find a function signature. */
+       if (opt->funcname && funcname_needed)
+               show_funcname_line(opt, name, buf, bol, cur);
+
+       /* Back forward. */
+       while (cur < lno) {
+               char *eol = bol, sign = (cur == funcname_lno) ? '=' : '-';
+
+               while (*eol != '\n')
+                       eol++;
+               show_line(opt, bol, eol, name, cur, sign);
+               bol = eol + 1;
+               cur++;
+       }
+}
+
 static int grep_buffer_1(struct grep_opt *opt, const char *name,
                         char *buf, unsigned long size, int collect_hits)
 {
        char *bol = buf;
        unsigned long left = size;
        unsigned lno = 1;
-       struct pre_context_line {
-               char *bol;
-               char *eol;
-       } *prev = NULL, *pcl;
        unsigned last_hit = 0;
-       unsigned last_shown = 0;
        int binary_match_only = 0;
-       const char *hunk_mark = "";
        unsigned count = 0;
        enum grep_context ctx = GREP_CONTEXT_HEAD;
+       xdemitconf_t xecfg;
+
+       opt->last_shown = 0;
 
        if (buffer_is_binary(buf, size)) {
                switch (opt->binary) {
@@ -550,10 +635,16 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                }
        }
 
-       if (opt->pre_context)
-               prev = xcalloc(opt->pre_context, sizeof(*prev));
-       if (opt->pre_context || opt->post_context)
-               hunk_mark = "--\n";
+       memset(&xecfg, 0, sizeof(xecfg));
+       if (opt->funcname && !opt->unmatch_name_only && !opt->status_only &&
+           !opt->name_only && !binary_match_only && !collect_hits) {
+               struct userdiff_driver *drv = userdiff_find_by_path(name);
+               if (drv && drv->funcname.pattern) {
+                       const struct userdiff_funcname *pe = &drv->funcname;
+                       xdiff_set_find_func(&xecfg, pe->pattern, pe->cflags);
+                       opt->priv = &xecfg;
+               }
+       }
 
        while (left) {
                char *eol, ch;
@@ -601,45 +692,20 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                         * the context which is nonsense, but the user
                         * deserves to get that ;-).
                         */
-                       if (opt->pre_context) {
-                               unsigned from;
-                               if (opt->pre_context < lno)
-                                       from = lno - opt->pre_context;
-                               else
-                                       from = 1;
-                               if (from <= last_shown)
-                                       from = last_shown + 1;
-                               if (last_shown && from != last_shown + 1)
-                                       fputs(hunk_mark, stdout);
-                               while (from < lno) {
-                                       pcl = &prev[lno-from-1];
-                                       show_line(opt, pcl->bol, pcl->eol,
-                                                 name, from, '-');
-                                       from++;
-                               }
-                               last_shown = lno-1;
-                       }
-                       if (last_shown && lno != last_shown + 1)
-                               fputs(hunk_mark, stdout);
+                       if (opt->pre_context)
+                               show_pre_context(opt, name, buf, bol, lno);
+                       else if (opt->funcname)
+                               show_funcname_line(opt, name, buf, bol, lno);
                        if (!opt->count)
                                show_line(opt, bol, eol, name, lno, ':');
-                       last_shown = last_hit = lno;
+                       last_hit = lno;
                }
                else if (last_hit &&
                         lno <= last_hit + opt->post_context) {
                        /* If the last hit is within the post context,
                         * we need to show this line.
                         */
-                       if (last_shown && lno != last_shown + 1)
-                               fputs(hunk_mark, stdout);
                        show_line(opt, bol, eol, name, lno, '-');
-                       last_shown = lno;
-               }
-               if (opt->pre_context) {
-                       memmove(prev+1, prev,
-                               (opt->pre_context-1) * sizeof(*prev));
-                       prev->bol = bol;
-                       prev->eol = eol;
                }
 
        next_line:
@@ -650,7 +716,6 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                lno++;
        }
 
-       free(prev);
        if (collect_hits)
                return 0;
 
@@ -662,6 +727,9 @@ static int grep_buffer_1(struct grep_opt *opt, const char *name,
                return 1;
        }
 
+       xdiff_clear_find_func(&xecfg);
+       opt->priv = NULL;
+
        /* NEEDSWORK:
         * The real "grep -c foo *.c" gives many "bar.c:0" lines,
         * which feels mostly useless but sometimes useful.  Maybe
diff --git a/grep.h b/grep.h
index 464e272edf4594539915a89d68628fd3959b8ee3..f00db0e40273c7cca1695ddc6be00921f9da8ef4 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -79,11 +79,15 @@ struct grep_opt {
        int pathname;
        int null_following_name;
        int color;
+       int funcname;
        char color_match[COLOR_MAXLEN];
        const char *color_external;
        int regflags;
        unsigned pre_context;
        unsigned post_context;
+       unsigned last_shown;
+       int show_hunk_mark;
+       void *priv;
 };
 
 extern void append_grep_pattern(struct grep_opt *opt, const char *pat, const char *origin, int no, enum grep_pat_token t);
index 47cf43c3cdd2bcd7638360a130fc8ef04ce132e9..9455dd0709aeb81436ef2e3e36422578d66ce8e7 100644 (file)
@@ -29,7 +29,7 @@ static void hash_object(const char *path, const char *type, int write_object,
        int fd;
        fd = open(path, O_RDONLY);
        if (fd < 0)
-               die("Cannot open %s", path);
+               die_errno("Cannot open '%s'", path);
        hash_fd(fd, type, write_object, vpath);
 }
 
diff --git a/help.c b/help.c
index fd87bb5aeec82beec600be46248b19b13bb33804..6c46d8b4949c4ad70ca6abc7edb4f7926c889a5a 100644 (file)
--- a/help.c
+++ b/help.c
@@ -100,7 +100,7 @@ static void pretty_print_string_list(struct cmdnames *cmds, int longest)
 
        if (space < max_cols)
                cols = max_cols / space;
-       rows = (cmds->cnt + cols - 1) / cols;
+       rows = DIV_ROUND_UP(cmds->cnt, cols);
 
        for (i = 0; i < rows; i++) {
                printf("  ");
index 8cc8ee0dfd5c047b2523f63346bf9b30094c1386..00e83dcec1d973b069d4c75105aed96634b00994 100644 (file)
@@ -193,6 +193,8 @@ static char *xml_entities(char *s)
                case '&':
                        strbuf_addstr(&buf, "&amp;");
                        break;
+               case 0:
+                       return strbuf_detach(&buf, NULL);
                }
                s++;
        }
diff --git a/http.c b/http.c
index b0499482455e256a3c2c9b6498b21c32d328c5c9..14d535747d943c2db3d45d82439f3ee9633ed2d9 100644 (file)
--- a/http.c
+++ b/http.c
@@ -33,6 +33,17 @@ static int curl_ftp_no_epsv;
 static const char *curl_http_proxy;
 static char *user_name, *user_pass;
 
+#if LIBCURL_VERSION_NUM >= 0x071700
+/* Use CURLOPT_KEYPASSWD as is */
+#elif LIBCURL_VERSION_NUM >= 0x070903
+#define CURLOPT_KEYPASSWD CURLOPT_SSLKEYPASSWD
+#else
+#define CURLOPT_KEYPASSWD CURLOPT_SSLCERTPASSWD
+#endif
+
+static char *ssl_cert_password;
+static int ssl_cert_password_required;
+
 static struct curl_slist *pragma_header;
 static struct curl_slist *no_pragma_header;
 
@@ -136,6 +147,11 @@ static int http_options(const char *var, const char *value, void *cb)
 #endif
        if (!strcmp("http.sslcainfo", var))
                return git_config_string(&ssl_cainfo, var, value);
+       if (!strcmp("http.sslcertpasswordprotected", var)) {
+               if (git_config_bool(var, value))
+                       ssl_cert_password_required = 1;
+               return 0;
+       }
 #ifdef USE_CURL_MULTI
        if (!strcmp("http.maxrequests", var)) {
                max_requests = git_config_int(var, value);
@@ -174,6 +190,22 @@ static void init_curl_http_auth(CURL *result)
        }
 }
 
+static int has_cert_password(void)
+{
+       if (ssl_cert_password != NULL)
+               return 1;
+       if (ssl_cert == NULL || ssl_cert_password_required != 1)
+               return 0;
+       /* Only prompt the user once. */
+       ssl_cert_password_required = -1;
+       ssl_cert_password = getpass("Certificate Password: ");
+       if (ssl_cert_password != NULL) {
+               ssl_cert_password = xstrdup(ssl_cert_password);
+               return 1;
+       } else
+               return 0;
+}
+
 static CURL *get_curl_handle(void)
 {
        CURL *result = curl_easy_init();
@@ -196,6 +228,8 @@ static CURL *get_curl_handle(void)
 
        if (ssl_cert != NULL)
                curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
+       if (has_cert_password())
+               curl_easy_setopt(result, CURLOPT_KEYPASSWD, ssl_cert_password);
 #if LIBCURL_VERSION_NUM >= 0x070903
        if (ssl_key != NULL)
                curl_easy_setopt(result, CURLOPT_SSLKEY, ssl_key);
@@ -339,8 +373,13 @@ void http_init(struct remote *remote)
        if (getenv("GIT_CURL_FTP_NO_EPSV"))
                curl_ftp_no_epsv = 1;
 
-       if (remote && remote->url && remote->url[0])
+       if (remote && remote->url && remote->url[0]) {
                http_auth_init(remote->url[0]);
+               if (!ssl_cert_password_required &&
+                   getenv("GIT_SSL_CERT_PASSWORD_PROTECTED") &&
+                   !prefixcmp(remote->url[0], "https://"))
+                       ssl_cert_password_required = 1;
+       }
 
 #ifndef NO_CURL_EASY_DUPHANDLE
        curl_default = get_curl_handle();
@@ -383,6 +422,13 @@ void http_cleanup(void)
                free((void *)curl_http_proxy);
                curl_http_proxy = NULL;
        }
+
+       if (ssl_cert_password != NULL) {
+               memset(ssl_cert_password, 0, strlen(ssl_cert_password));
+               free(ssl_cert_password);
+               ssl_cert_password = NULL;
+       }
+       ssl_cert_password_required = 0;
 }
 
 struct active_request_slot *get_active_slot(void)
@@ -958,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];
@@ -972,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);
@@ -989,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);
 
@@ -1013,6 +1057,8 @@ struct http_pack_request *new_http_pack_request(
 
 abort:
        free(filename);
+       free(preq->url);
+       free(preq);
        return NULL;
 }
 
@@ -1052,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;
@@ -1106,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
@@ -1143,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;
+                       }
                }
        }
 
@@ -1152,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);
 
        /*
@@ -1172,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;
 }
index c72cbd406a7880361f262f43daff62a9f453628d..340074fc793e8e7534bb168784a7051af6d81b34 100644 (file)
@@ -143,7 +143,7 @@ static void *fill(int min)
                if (ret <= 0) {
                        if (!ret)
                                die("early EOF");
-                       die("read error on input: %s", strerror(errno));
+                       die_errno("read error on input");
                }
                input_len += ret;
                if (from_stdin)
@@ -178,13 +178,12 @@ static char *open_pack_file(char *pack_name)
                } else
                        output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
                if (output_fd < 0)
-                       die("unable to create %s: %s", pack_name, strerror(errno));
+                       die_errno("unable to create '%s'", pack_name);
                pack_fd = output_fd;
        } else {
                input_fd = open(pack_name, O_RDONLY);
                if (input_fd < 0)
-                       die("cannot open packfile '%s': %s",
-                           pack_name, strerror(errno));
+                       die_errno("cannot open packfile '%s'", pack_name);
                output_fd = -1;
                pack_fd = input_fd;
        }
@@ -370,7 +369,7 @@ static void *get_data_from_pack(struct object_entry *obj)
        do {
                ssize_t n = pread(pack_fd, data + rdy, len - rdy, from + rdy);
                if (n < 0)
-                       die("cannot pread pack file: %s", strerror(errno));
+                       die_errno("cannot pread pack file");
                if (!n)
                        die("premature end of pack file, %lu bytes missing",
                            len - rdy);
@@ -631,7 +630,7 @@ static void parse_pack_objects(unsigned char *sha1)
 
        /* If input_fd is a file, we should have reached its end now. */
        if (fstat(input_fd, &st))
-               die("cannot fstat packfile: %s", strerror(errno));
+               die_errno("cannot fstat packfile");
        if (S_ISREG(st.st_mode) &&
                        lseek(input_fd, 0, SEEK_CUR) - input_len != st.st_size)
                die("pack has junk at the end");
@@ -788,7 +787,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                fsync_or_die(output_fd, curr_pack_name);
                err = close(output_fd);
                if (err)
-                       die("error while closing pack file: %s", strerror(errno));
+                       die_errno("error while closing pack file");
        }
 
        if (keep_msg) {
@@ -801,16 +800,16 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
 
                if (keep_fd < 0) {
                        if (errno != EEXIST)
-                               die("cannot write keep file '%s' (%s)",
-                                   keep_name, strerror(errno));
+                               die_errno("cannot write keep file '%s'",
+                                         keep_name);
                } else {
                        if (keep_msg_len > 0) {
                                write_or_die(keep_fd, keep_msg, keep_msg_len);
                                write_or_die(keep_fd, "\n", 1);
                        }
                        if (close(keep_fd) != 0)
-                               die("cannot close written keep file '%s' (%s)",
-                                   keep_name, strerror(errno));
+                               die_errno("cannot close written keep file '%s'",
+                                   keep_name);
                        report = "keep";
                }
        }
index 31c74578f6b82d859e038ddf9ca43d3b5fd9429a..2d6b6d6cb1d2bc2d334bf058feb3444e94b5a781 100644 (file)
@@ -152,7 +152,7 @@ static void create_temp(mmfile_t *src, char *path)
        strcpy(path, ".merge_file_XXXXXX");
        fd = xmkstemp(path);
        if (write_in_full(fd, src->ptr, src->size) != src->size)
-               die("unable to write temp-file");
+               die_errno("unable to write temp-file");
        close(fd);
 }
 
index 53cad9605bf1bb599b9c0ceb14788afa719dd5ac..d415c4188d181fbdc765fff90fbbc4897ced53ad 100644 (file)
@@ -438,7 +438,7 @@ static void flush_buffer(int fd, const char *buf, unsigned long size)
                        /* Ignore epipe */
                        if (errno == EPIPE)
                                break;
-                       die("merge-recursive: %s", strerror(errno));
+                       die_errno("merge-recursive");
                } else if (!ret) {
                        die("merge-recursive: disk full?");
                }
@@ -554,7 +554,7 @@ static void update_file_flags(struct merge_options *o,
                                mode = 0666;
                        fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, mode);
                        if (fd < 0)
-                               die("failed to open %s: %s", path, strerror(errno));
+                               die_errno("failed to open '%s'", path);
                        flush_buffer(fd, buf, size);
                        close(fd);
                } else if (S_ISLNK(mode)) {
@@ -562,7 +562,7 @@ static void update_file_flags(struct merge_options *o,
                        safe_create_leading_directories_const(path);
                        unlink(path);
                        if (symlink(lnk, path))
-                               die("failed to symlink %s: %s", path, strerror(errno));
+                               die_errno("failed to symlink '%s'", path);
                        free(lnk);
                } else
                        die("do not know what to do with %06o %s '%s'",
diff --git a/mktag.c b/mktag.c
index 99a356e9ee75cb247d80ed6dc0b251ceb0bd9e46..a609e3ebd106ed985fa2065dc4af6565e5a379a6 100644 (file)
--- a/mktag.c
+++ b/mktag.c
@@ -165,7 +165,7 @@ int main(int argc, char **argv)
        setup_git_directory();
 
        if (strbuf_read(&buf, 0, 4096) < 0) {
-               die("could not read from stdin");
+               die_errno("could not read from stdin");
        }
 
        /* Verify it for some basic sanity: it needs to start with
index 301fc60eae1ad53721851a272a0fbd0192881801..7f43f8ac3398fb2dbe393f08811989b0a8d4e2dd 100644 (file)
@@ -93,8 +93,7 @@ int pack_refs(unsigned int flags)
                                       LOCK_DIE_ON_ERROR);
        cbdata.refs_file = fdopen(fd, "w");
        if (!cbdata.refs_file)
-               die("unable to create ref-pack file structure (%s)",
-                   strerror(errno));
+               die_errno("unable to create ref-pack file structure");
 
        /* perhaps other traits later as well */
        fprintf(cbdata.refs_file, "# pack-refs with: peeled \n");
@@ -103,7 +102,7 @@ int pack_refs(unsigned int flags)
        if (ferror(cbdata.refs_file))
                die("failed to write ref-pack file");
        if (fflush(cbdata.refs_file) || fsync(fd) || fclose(cbdata.refs_file))
-               die("failed to write ref-pack file (%s)", strerror(errno));
+               die_errno("failed to write ref-pack file");
        /*
         * Since the lock file was fdopen()'ed and then fclose()'ed above,
         * assign -1 to the lock file descriptor so that commit_lock_file()
@@ -111,7 +110,7 @@ int pack_refs(unsigned int flags)
         */
        packed.fd = -1;
        if (commit_lock_file(&packed) < 0)
-               die("unable to overwrite old ref-pack file (%s)", strerror(errno));
+               die_errno("unable to overwrite old ref-pack file");
        if (cbdata.flags & PACK_REFS_PRUNE)
                prune_refs(cbdata.ref_to_prune);
        return 0;
index 1de53c8934c03b46604e94cbf4237ad5ffc57f83..77a0465be6f6a79814aa3c009612736770b342a1 100644 (file)
@@ -149,8 +149,7 @@ void discard_revindex(void)
        if (pack_revindex_hashsz) {
                int i;
                for (i = 0; i < pack_revindex_hashsz; i++)
-                       if (pack_revindex[i].revindex)
-                               free(pack_revindex[i].revindex);
+                       free(pack_revindex[i].revindex);
                free(pack_revindex);
                pack_revindex_hashsz = 0;
        }
index 7053538f4cf44e15a788ab46dfb680ee85ce4fc2..741efcd93bd2a2c105bfe2d771d32857ce2ae816 100644 (file)
@@ -51,7 +51,7 @@ char *write_idx_file(char *index_name, struct pack_idx_entry **objects,
                fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
        }
        if (fd < 0)
-               die("unable to create %s: %s", index_name, strerror(errno));
+               die_errno("unable to create '%s'", index_name);
        f = sha1fd(fd, index_name);
 
        /* if last object's offset is >= 2^31 we should use index V2 */
@@ -174,11 +174,11 @@ void fixup_pack_header_footer(int pack_fd,
        git_SHA1_Init(&new_sha1_ctx);
 
        if (lseek(pack_fd, 0, SEEK_SET) != 0)
-               die("Failed seeking to start of %s: %s", pack_name, strerror(errno));
+               die_errno("Failed seeking to start of '%s'", pack_name);
        if (read_in_full(pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
-               die("Unable to reread header of %s: %s", pack_name, strerror(errno));
+               die_errno("Unable to reread header of '%s'", pack_name);
        if (lseek(pack_fd, 0, SEEK_SET) != 0)
-               die("Failed seeking to start of %s: %s", pack_name, strerror(errno));
+               die_errno("Failed seeking to start of '%s'", pack_name);
        git_SHA1_Update(&old_sha1_ctx, &hdr, sizeof(hdr));
        hdr.hdr_entries = htonl(object_count);
        git_SHA1_Update(&new_sha1_ctx, &hdr, sizeof(hdr));
@@ -195,7 +195,7 @@ void fixup_pack_header_footer(int pack_fd,
                if (!n)
                        break;
                if (n < 0)
-                       die("Failed to checksum %s: %s", pack_name, strerror(errno));
+                       die_errno("Failed to checksum '%s'", pack_name);
                git_SHA1_Update(&new_sha1_ctx, buf, n);
 
                aligned_sz -= n;
index f7ce523a615b121b8a0825e459c1b75f24253c85..3b71fbb5410332a3f11ca0e450a614b3345a687b 100644 (file)
@@ -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");
 
index aba30671dccd9e90686363a766df3eb25d4a8e20..b32587ad7cbf5ac3923764d68d1ccd197955e660 100644 (file)
@@ -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.
index f5d00863a6234c16db33637d19fefd2014780e87..b691abebd79b40f096b28b5dbcb433f01bc0e949 100644 (file)
@@ -28,7 +28,7 @@ ssize_t safe_write(int fd, const void *buf, ssize_t n)
                }
                if (!ret)
                        die("write error (disk full?)");
-               die("write error (%s)", strerror(errno));
+               die_errno("write error");
        }
        return nn;
 }
@@ -67,7 +67,7 @@ static void safe_read(int fd, void *buffer, unsigned size)
 {
        ssize_t ret = read_in_full(fd, buffer, size);
        if (ret < 0)
-               die("read error (%s)", strerror(errno));
+               die_errno("read error");
        else if (ret < size)
                die("The remote end hung up unexpectedly");
 }
index 88edc5f8a9d5384e19426e6adb40e08b34d3adf2..92899333c2d8edbed71fdd3a43e19f25a10e5b03 100644 (file)
@@ -34,7 +34,9 @@ static void *preload_thread(void *_data)
        struct thread_data *p = _data;
        struct index_state *index = p->index;
        struct cache_entry **cep = index->cache + p->offset;
+       struct cache_def cache;
 
+       memset(&cache, 0, sizeof(cache));
        nr = p->nr;
        if (nr + p->offset > index->cache_nr)
                nr = index->cache_nr - p->offset;
@@ -49,6 +51,8 @@ static void *preload_thread(void *_data)
                        continue;
                if (!ce_path_match(ce, p->pathspec))
                        continue;
+               if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
+                       continue;
                if (lstat(ce->name, &st))
                        continue;
                if (ie_match_stat(index, ce, &st, CE_MATCH_RACY_IS_DIRTY))
@@ -72,7 +76,7 @@ static void preload_index(struct index_state *index, const char **pathspec)
        if (threads > MAX_PARALLEL)
                threads = MAX_PARALLEL;
        offset = 0;
-       work = (index->cache_nr + threads - 1) / threads;
+       work = DIV_ROUND_UP(index->cache_nr, threads);
        for (i = 0; i < threads; i++) {
                struct thread_data *p = data+i;
                p->index = index;
index 3f587110cb9d7be1890b7db68a0bdac35d48cd35..4e3e272ee409de66de2059a8be475fddcaa4dc28 100644 (file)
@@ -638,7 +638,7 @@ int add_file_to_index(struct index_state *istate, const char *path, int flags)
 {
        struct stat st;
        if (lstat(path, &st))
-               die("%s: unable to stat (%s)", path, strerror(errno));
+               die_errno("unable to stat '%s'", path);
        return add_to_index(istate, path, &st, flags);
 }
 
@@ -1251,11 +1251,11 @@ int read_index_from(struct index_state *istate, const char *path)
        if (fd < 0) {
                if (errno == ENOENT)
                        return 0;
-               die("index file open failed (%s)", strerror(errno));
+               die_errno("index file open failed");
        }
 
        if (fstat(fd, &st))
-               die("cannot stat the open index (%s)", strerror(errno));
+               die_errno("cannot stat the open index");
 
        errno = EINVAL;
        mmap_size = xsize_t(st.st_size);
@@ -1265,7 +1265,7 @@ int read_index_from(struct index_state *istate, const char *path)
        mmap = xmmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
        close(fd);
        if (mmap == MAP_FAILED)
-               die("unable to map index file");
+               die_errno("unable to map index file");
 
        hdr = mmap;
        if (verify_hdr(hdr, mmap_size) < 0)
diff --git a/refs.c b/refs.c
index 24438c652fe4e09aaa1ba6dab283b8e59c24c1a7..e49eaa3089c39028e3c0af90422672db63fff198 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -531,9 +531,10 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
 {
        if (strncmp(base, entry->name, trim))
                return 0;
+       /* Is this a "negative ref" that represents a deleted ref? */
+       if (is_null_sha1(entry->sha1))
+               return 0;
        if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
-               if (is_null_sha1(entry->sha1))
-                       return 0;
                if (!has_sha1_file(entry->sha1)) {
                        error("%s does not point to a valid object!", entry->name);
                        return 0;
@@ -1418,7 +1419,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
        logfile = git_path("logs/%s", ref);
        logfd = open(logfile, O_RDONLY, 0);
        if (logfd < 0)
-               die("Unable to read log %s: %s", logfile, strerror(errno));
+               die_errno("Unable to read log '%s'", logfile);
        fstat(logfd, &st);
        if (!st.st_size)
                die("Log %s is empty.", logfile);
@@ -1525,8 +1526,10 @@ int for_each_recent_reflog_ent(const char *ref, each_reflog_ent_fn fn, long ofs,
                if (fstat(fileno(logfp), &statbuf) ||
                    statbuf.st_size < ofs ||
                    fseek(logfp, -ofs, SEEK_END) ||
-                   fgets(buf, sizeof(buf), logfp))
+                   fgets(buf, sizeof(buf), logfp)) {
+                       fclose(logfp);
                        return -1;
+               }
        }
 
        while (fgets(buf, sizeof(buf), logfp)) {
index 733ba57494715e00427350af4175fe67c390ec34..c3ada2d72b9b3f7411ffe420030768e73a003923 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1277,7 +1277,7 @@ struct ref *get_remote_ref(const struct ref *remote_refs, const char *name)
 
 static struct ref *get_local_ref(const char *name)
 {
-       if (!name)
+       if (!name || name[0] == '\0')
                return NULL;
 
        if (!prefixcmp(name, "refs/"))
index a31434bdc8ed19674d3c4863e009af0c40d75e73..9f5dac5f1d8a8a654a2ab77e8e26fb134c7e36fa 100644 (file)
@@ -133,7 +133,7 @@ void mark_parents_uninteresting(struct commit *commit)
 static void add_pending_object_with_mode(struct rev_info *revs, struct object *obj, const char *name, unsigned mode)
 {
        if (revs->no_walk && (obj->flags & UNINTERESTING))
-               die("object ranges do not make sense when not walking revisions");
+               revs->no_walk = 0;
        if (revs->reflog_info && obj->type == OBJ_COMMIT &&
                        add_reflog_for_walk(revs->reflog_info,
                                (struct commit *)obj, name))
index 71f83368c4e97357c782658135982e89c13ae414..f3e7abb7de799a14a8f792195992c4d9d1495c6f 100644 (file)
@@ -109,8 +109,8 @@ fail_pipe:
                }
 
                if (cmd->dir && chdir(cmd->dir))
-                       die("exec %s: cd to %s failed (%s)", cmd->argv[0],
-                           cmd->dir, strerror(errno));
+                       die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
+                           cmd->dir);
                if (cmd->env) {
                        for (; *cmd->env; cmd->env++) {
                                if (strchr(*cmd->env, '='))
diff --git a/setup.c b/setup.c
index ebd60de9ce5b52f348819a6a390c15b8dc08d2ff..e3781b656d77be94e04c5229e6a190c5fc496490 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -81,7 +81,7 @@ void verify_filename(const char *prefix, const char *arg)
        if (errno == ENOENT)
                die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
                    "Use '--' to separate paths from revisions", arg);
-       die("'%s': %s", arg, strerror(errno));
+       die_errno("failed to stat '%s'", arg);
 }
 
 /*
@@ -103,7 +103,7 @@ void verify_non_filename(const char *prefix, const char *arg)
                die("ambiguous argument '%s': both revision and filename\n"
                    "Use '--' to separate filenames from revisions", arg);
        if (errno != ENOENT && errno != ENOTDIR)
-               die("'%s': %s", arg, strerror(errno));
+               die_errno("failed to stat '%s'", arg);
 }
 
 const char **get_pathspec(const char *prefix, const char **pathspec)
@@ -257,7 +257,7 @@ const char *read_gitfile_gently(const char *path)
                return NULL;
        fd = open(path, O_RDONLY);
        if (fd < 0)
-               die("Error opening %s: %s", path, strerror(errno));
+               die_errno("Error opening '%s'", path);
        buf = xmalloc(st.st_size + 1);
        len = read_in_full(fd, buf, st.st_size);
        close(fd);
@@ -327,7 +327,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
                                return NULL;
                        set_git_dir(make_absolute_path(gitdirenv));
                        if (chdir(work_tree_env) < 0)
-                               die ("Could not chdir to %s", work_tree_env);
+                               die_errno ("Could not chdir to '%s'", work_tree_env);
                        strcat(buffer, "/");
                        return retval;
                }
@@ -339,7 +339,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
        }
 
        if (!getcwd(cwd, sizeof(cwd)-1))
-               die("Unable to read current working directory");
+               die_errno("Unable to read current working directory");
 
        ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
        if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
@@ -382,14 +382,14 @@ const char *setup_git_directory_gently(int *nongit_ok)
                if (offset <= ceil_offset) {
                        if (nongit_ok) {
                                if (chdir(cwd))
-                                       die("Cannot come back to cwd");
+                                       die_errno("Cannot come back to cwd");
                                *nongit_ok = 1;
                                return NULL;
                        }
                        die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
                }
                if (chdir(".."))
-                       die("Cannot change to %s/..: %s", cwd, strerror(errno));
+                       die_errno("Cannot change to '%s/..'", cwd);
        }
 
        inside_git_dir = 0;
@@ -493,10 +493,10 @@ const char *setup_git_directory(void)
                static char buffer[PATH_MAX + 1];
                char *rel;
                if (retval && chdir(retval))
-                       die ("Could not jump back into original cwd");
+                       die_errno ("Could not jump back into original cwd");
                rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
                if (rel && *rel && chdir(get_git_work_tree()))
-                       die ("Could not jump to working directory");
+                       die_errno ("Could not jump to working directory");
                return rel && *rel ? strcat(rel, "/") : NULL;
        }
 
index 8f5fe62d545ace21c338cd554c76bac5d5acb431..1d996a199036c115d46c1d630d53edf29b3b78e7 100644 (file)
@@ -1170,7 +1170,7 @@ unsigned long unpack_object_header_buffer(const unsigned char *buf,
        size = c & 15;
        shift = 4;
        while (c & 0x80) {
-               if (len <= used || sizeof(long) * 8 <= shift) {
+               if (len <= used || bitsizeof(long) <= shift) {
                        error("bad object header");
                        return 0;
                }
@@ -2286,7 +2286,7 @@ static void close_sha1_file(int fd)
        if (fsync_object_files)
                fsync_or_die(fd, "sha1 file");
        if (close(fd) != 0)
-               die("error when closing sha1 file (%s)", strerror(errno));
+               die_errno("error when closing sha1 file");
 }
 
 /* Size of directory component, including the ending '/' */
index 904bcd96a54a1cc33386a56a16d07dce34cbb90b..44bb62d270739a232e87c90c05ce89fcc86bc15b 100644 (file)
@@ -777,8 +777,6 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
        for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb);
        if (cb.cnt < nth) {
                cb.cnt = 0;
-               for (i = 0; i < nth; i++)
-                       strbuf_release(&cb.buf[i]);
                for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
        }
        if (cb.cnt < nth)
diff --git a/shell.c b/shell.c
index b968be79f487f8d93e205fd0a844a206e24f21f8..e4864e04da3b0e237342c2ca0548c0ec0082c171 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -60,7 +60,7 @@ int main(int argc, char **argv)
        while (devnull_fd >= 0 && devnull_fd <= 2)
                devnull_fd = dup(devnull_fd);
        if (devnull_fd == -1)
-               die("opening /dev/null failed (%s)", strerror(errno));
+               die_errno("opening /dev/null failed");
        close (devnull_fd);
 
        /*
index 8dcd63261fff56e54c233224c6014b146d9bbeb3..4bdded39c5c5fc491189661bfeca045472970f5b 100644 (file)
@@ -32,19 +32,13 @@ static int longest_path_match(const char *name_a, int len_a,
        return match_len;
 }
 
-static struct cache_def {
-       char path[PATH_MAX + 1];
-       int len;
-       int flags;
-       int track_flags;
-       int prefix_len_stat_func;
-} cache;
+static struct cache_def default_cache;
 
-static inline void reset_lstat_cache(void)
+static inline void reset_lstat_cache(struct cache_def *cache)
 {
-       cache.path[0] = '\0';
-       cache.len = 0;
-       cache.flags = 0;
+       cache->path[0] = '\0';
+       cache->len = 0;
+       cache->flags = 0;
        /*
         * The track_flags and prefix_len_stat_func members is only
         * set by the safeguard rule inside lstat_cache()
@@ -70,23 +64,23 @@ static inline void reset_lstat_cache(void)
  * of the prefix, where the cache should use the stat() function
  * instead of the lstat() function to test each path component.
  */
-static int lstat_cache(const char *name, int len,
+static int lstat_cache(struct cache_def *cache, const char *name, int len,
                       int track_flags, int prefix_len_stat_func)
 {
        int match_len, last_slash, last_slash_dir, previous_slash;
        int match_flags, ret_flags, save_flags, max_len, ret;
        struct stat st;
 
-       if (cache.track_flags != track_flags ||
-           cache.prefix_len_stat_func != prefix_len_stat_func) {
+       if (cache->track_flags != track_flags ||
+           cache->prefix_len_stat_func != prefix_len_stat_func) {
                /*
                 * As a safeguard rule we clear the cache if the
                 * values of track_flags and/or prefix_len_stat_func
                 * does not match with the last supplied values.
                 */
-               reset_lstat_cache();
-               cache.track_flags = track_flags;
-               cache.prefix_len_stat_func = prefix_len_stat_func;
+               reset_lstat_cache(cache);
+               cache->track_flags = track_flags;
+               cache->prefix_len_stat_func = prefix_len_stat_func;
                match_len = last_slash = 0;
        } else {
                /*
@@ -94,10 +88,10 @@ static int lstat_cache(const char *name, int len,
                 * the 2 "excluding" path types.
                 */
                match_len = last_slash =
-                       longest_path_match(name, len, cache.path, cache.len,
+                       longest_path_match(name, len, cache->path, cache->len,
                                           &previous_slash);
-               match_flags = cache.flags & track_flags & (FL_NOENT|FL_SYMLINK);
-               if (match_flags && match_len == cache.len)
+               match_flags = cache->flags & track_flags & (FL_NOENT|FL_SYMLINK);
+               if (match_flags && match_len == cache->len)
                        return match_flags;
                /*
                 * If we now have match_len > 0, we would know that
@@ -121,18 +115,18 @@ static int lstat_cache(const char *name, int len,
        max_len = len < PATH_MAX ? len : PATH_MAX;
        while (match_len < max_len) {
                do {
-                       cache.path[match_len] = name[match_len];
+                       cache->path[match_len] = name[match_len];
                        match_len++;
                } while (match_len < max_len && name[match_len] != '/');
                if (match_len >= max_len && !(track_flags & FL_FULLPATH))
                        break;
                last_slash = match_len;
-               cache.path[last_slash] = '\0';
+               cache->path[last_slash] = '\0';
 
                if (last_slash <= prefix_len_stat_func)
-                       ret = stat(cache.path, &st);
+                       ret = stat(cache->path, &st);
                else
-                       ret = lstat(cache.path, &st);
+                       ret = lstat(cache->path, &st);
 
                if (ret) {
                        ret_flags = FL_LSTATERR;
@@ -156,9 +150,9 @@ static int lstat_cache(const char *name, int len,
         */
        save_flags = ret_flags & track_flags & (FL_NOENT|FL_SYMLINK);
        if (save_flags && last_slash > 0 && last_slash <= PATH_MAX) {
-               cache.path[last_slash] = '\0';
-               cache.len = last_slash;
-               cache.flags = save_flags;
+               cache->path[last_slash] = '\0';
+               cache->len = last_slash;
+               cache->flags = save_flags;
        } else if ((track_flags & FL_DIR) &&
                   last_slash_dir > 0 && last_slash_dir <= PATH_MAX) {
                /*
@@ -172,11 +166,11 @@ static int lstat_cache(const char *name, int len,
                 * can still cache the path components before the last
                 * one (the found symlink or non-existing component).
                 */
-               cache.path[last_slash_dir] = '\0';
-               cache.len = last_slash_dir;
-               cache.flags = FL_DIR;
+               cache->path[last_slash_dir] = '\0';
+               cache->len = last_slash_dir;
+               cache->flags = FL_DIR;
        } else {
-               reset_lstat_cache();
+               reset_lstat_cache(cache);
        }
        return ret_flags;
 }
@@ -188,16 +182,17 @@ static int lstat_cache(const char *name, int len,
 void invalidate_lstat_cache(const char *name, int len)
 {
        int match_len, previous_slash;
+       struct cache_def *cache = &default_cache;       /* FIXME */
 
-       match_len = longest_path_match(name, len, cache.path, cache.len,
+       match_len = longest_path_match(name, len, cache->path, cache->len,
                                       &previous_slash);
        if (len == match_len) {
-               if ((cache.track_flags & FL_DIR) && previous_slash > 0) {
-                       cache.path[previous_slash] = '\0';
-                       cache.len = previous_slash;
-                       cache.flags = FL_DIR;
+               if ((cache->track_flags & FL_DIR) && previous_slash > 0) {
+                       cache->path[previous_slash] = '\0';
+                       cache->len = previous_slash;
+                       cache->flags = FL_DIR;
                } else {
-                       reset_lstat_cache();
+                       reset_lstat_cache(cache);
                }
        }
 }
@@ -207,19 +202,26 @@ void invalidate_lstat_cache(const char *name, int len)
  */
 void clear_lstat_cache(void)
 {
-       reset_lstat_cache();
+       struct cache_def *cache = &default_cache;       /* FIXME */
+       reset_lstat_cache(cache);
 }
 
 #define USE_ONLY_LSTAT  0
 
+/*
+ * Return non-zero if path 'name' has a leading symlink component
+ */
+int threaded_has_symlink_leading_path(struct cache_def *cache, const char *name, int len)
+{
+       return lstat_cache(cache, name, len, FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) & FL_SYMLINK;
+}
+
 /*
  * Return non-zero if path 'name' has a leading symlink component
  */
 int has_symlink_leading_path(const char *name, int len)
 {
-       return lstat_cache(name, len,
-                          FL_SYMLINK|FL_DIR, USE_ONLY_LSTAT) &
-               FL_SYMLINK;
+       return threaded_has_symlink_leading_path(&default_cache, name, len);
 }
 
 /*
@@ -228,7 +230,8 @@ int has_symlink_leading_path(const char *name, int len)
  */
 int has_symlink_or_noent_leading_path(const char *name, int len)
 {
-       return lstat_cache(name, len,
+       struct cache_def *cache = &default_cache;       /* FIXME */
+       return lstat_cache(cache, name, len,
                           FL_SYMLINK|FL_NOENT|FL_DIR, USE_ONLY_LSTAT) &
                (FL_SYMLINK|FL_NOENT);
 }
@@ -242,7 +245,8 @@ int has_symlink_or_noent_leading_path(const char *name, int len)
  */
 int has_dirs_only_path(const char *name, int len, int prefix_len)
 {
-       return lstat_cache(name, len,
+       struct cache_def *cache = &default_cache;       /* FIXME */
+       return lstat_cache(cache, name, len,
                           FL_DIR|FL_FULLPATH, prefix_len) &
                FL_DIR;
 }
index bf816fc8505508c91999175ad6544a67febabb33..bd09390d3208d7eac362cd9cf45f7dde623c4ae6 100644 (file)
@@ -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 (file)
index 0000000..4b3b793
--- /dev/null
@@ -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
+}
index e3d846420dc09e7b24876b291b9e546ac0628ed3..5386504790deea55d127f053f7b714cd121a2d57 100755 (executable)
@@ -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
index 43ea283242c4afc25e53d4a8c894140793649717..83b7294010cd59d5438b6020868c699c01105595 100755 (executable)
@@ -459,6 +459,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 &&
index 997002d4c40dd8e66e3be5a701e3d99bab1c57c4..e5040580626f4df6159c929c1ad54f085f03d830 100755 (executable)
@@ -20,8 +20,7 @@ Extras
 
 EOF
 
-test_expect_success 'test --parseopt help output' '
-       git rev-parse --parseopt -- -h 2> output.err <<EOF
+cat > optionspec << EOF
 some-command [options] <args>...
 
 some-command does foo and bar!
@@ -37,7 +36,47 @@ C?        option C with an optional argument
 Extras
 extra1    line above used to cause a segfault but no longer does
 EOF
+
+test_expect_success 'test --parseopt help output' '
+       git rev-parse --parseopt -- -h 2> output.err < optionspec
        test_cmp expect.err output.err
 '
 
+cat > expect <<EOF
+set -- --foo --bar 'ham' -- 'arg'
+EOF
+
+test_expect_success 'test --parseopt' '
+       git rev-parse --parseopt -- --foo --bar=ham arg < optionspec > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'test --parseopt with mixed options and arguments' '
+       git rev-parse --parseopt -- --foo arg --bar=ham < optionspec > output &&
+       test_cmp expect output
+'
+
+cat > expect <<EOF
+set -- --foo -- 'arg' '--bar=ham'
+EOF
+
+test_expect_success 'test --parseopt with --' '
+       git rev-parse --parseopt -- --foo -- arg --bar=ham < optionspec > output &&
+       test_cmp expect output
+'
+
+test_expect_success 'test --parseopt --stop-at-non-option' '
+       git rev-parse --parseopt --stop-at-non-option -- --foo arg --bar=ham < optionspec > output &&
+       test_cmp expect output
+'
+
+cat > expect <<EOF
+set -- --foo -- '--' 'arg' '--bar=ham'
+EOF
+
+test_expect_success 'test --parseopt --keep-dashdash' '
+       git rev-parse --parseopt --keep-dashdash -- --foo -- arg --bar=ham < optionspec > output &&
+       test_cmp expect output
+'
+
 test_done
index a973628e8e3767ae017a6b18742bf4b66a68cdaa..4cae019521f988fe6a3eea0902d35dbbdae32dc8 100755 (executable)
@@ -10,7 +10,7 @@ that the result still makes sense.
 '
 . ./test-lib.sh
 
-. ../lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
 
 set_fake_editor
 
index 6533505218a51db369d03357603c2ad1926ce448..14a23cd8726fbb33df480133ed0466177916b7fb 100755 (executable)
@@ -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 (executable)
index 0000000..ee0a6cc
--- /dev/null
@@ -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
index 922a8941ed4720e2ad6dcd500a3759187aace969..531f5b795c197d942223db3cdc2d2030208e9beb 100755 (executable)
@@ -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"
index 4ea42e00dad9bf15e221e6eac5d91748cb0a07e7..a7602cf923d95a51643753ce44fa65cf406aba9c 100755 (executable)
@@ -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/t4038-diff-combined.sh b/t/t4038-diff-combined.sh
new file mode 100755 (executable)
index 0000000..2cf7e01
--- /dev/null
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+test_description='combined diff'
+
+. ./test-lib.sh
+
+setup_helper () {
+       one=$1 branch=$2 side=$3 &&
+
+       git branch $side $branch &&
+       for l in $one two three fyra
+       do
+               echo $l
+       done >file &&
+       git add file &&
+       test_tick &&
+       git commit -m $branch &&
+       git checkout $side &&
+       for l in $one two three quatro
+       do
+               echo $l
+       done >file &&
+       git add file &&
+       test_tick &&
+       git commit -m $side &&
+       test_must_fail git merge $branch &&
+       for l in $one three four
+       do
+               echo $l
+       done >file &&
+       git add file &&
+       test_tick &&
+       git commit -m "merge $branch into $side"
+}
+
+verify_helper () {
+       it=$1 &&
+
+       # Ignore lines that were removed only from the other parent
+       sed -e '
+               1,/^@@@/d
+               /^ -/d
+               s/^\(.\)./\1/
+       ' "$it" >"$it.actual.1" &&
+       sed -e '
+               1,/^@@@/d
+               /^- /d
+               s/^.\(.\)/\1/
+       ' "$it" >"$it.actual.2" &&
+
+       git diff "$it^" "$it" -- | sed -e '1,/^@@/d' >"$it.expect.1" &&
+       test_cmp "$it.expect.1" "$it.actual.1" &&
+
+       git diff "$it^2" "$it" -- | sed -e '1,/^@@/d' >"$it.expect.2" &&
+       test_cmp "$it.expect.2" "$it.actual.2"
+}
+
+test_expect_success setup '
+       >file &&
+       git add file &&
+       test_tick &&
+       git commit -m initial &&
+
+       git branch withone &&
+       git branch sansone &&
+
+       git checkout withone &&
+       setup_helper one withone sidewithone &&
+
+       git checkout sansone &&
+       setup_helper "" sansone sidesansone
+'
+
+test_expect_success 'check combined output (1)' '
+       git show sidewithone -- >sidewithone &&
+       verify_helper sidewithone
+'
+
+test_expect_failure 'check combined output (2)' '
+       git show sidesansone -- >sidesansone &&
+       verify_helper sidesansone
+'
+
+test_done
index f83322e513b96bb90e71ce39340515c6be0db186..fac2093d7f20d502f41f895d2ccdfccd5e639e2b 100755 (executable)
@@ -148,4 +148,26 @@ do
        done
 done
 
+create_patch () {
+       sed -e "s/_/ /" <<-\EOF
+               diff --git a/target b/target
+               index e69de29..8bd6648 100644
+               --- a/target
+               +++ b/target
+               @@ -0,0 +1,3 @@
+               +An empty line follows
+               +
+               +A line with trailing whitespace and no newline_
+               \ No newline at end of file
+       EOF
+}
+
+test_expect_success 'trailing whitespace & no newline at the end of file' '
+       >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 (executable)
index 0000000..bb1ffe3
--- /dev/null
@@ -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
index aad3894ad4c542d4ab894c2d57a035a89eb88258..48e0088b4780e133a450aea01092d9238e7122e9 100755 (executable)
@@ -149,6 +149,26 @@ test_expect_success 'git log --follow' '
 
 '
 
+cat > expect << EOF
+804a787 sixth
+394ef78 fifth
+5d31159 fourth
+EOF
+test_expect_success 'git log --no-walk <commits> sorts by commit time' '
+       git log --no-walk --oneline 5d31159 804a787 394ef78 > actual &&
+       test_cmp expect actual
+'
+
+cat > expect << EOF
+5d31159 fourth
+804a787 sixth
+394ef78 fifth
+EOF
+test_expect_success 'git show <commits> leaves list of commits as given' '
+       git show --oneline -s 5d31159 804a787 394ef78 > actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'setup case sensitivity tests' '
        echo case >one &&
        test_tick &&
index 16eadd6b68664884836976aafb6dcbb582603c09..1037a723fe74756f241346a077f4f3682dbbf45d 100755 (executable)
@@ -119,4 +119,24 @@ test_expect_success 'quickfetch should not copy from alternate' '
 
 '
 
+test_expect_success 'quickfetch should handle ~1000 refs (on Windows)' '
+
+       git gc &&
+       head=$(git rev-parse HEAD) &&
+       branchprefix="$head refs/heads/branch" &&
+       for i in 0 1 2 3 4 5 6 7 8 9; do
+               for j in 0 1 2 3 4 5 6 7 8 9; do
+                       for k in 0 1 2 3 4 5 6 7 8 9; do
+                               echo "$branchprefix$i$j$k" >> .git/packed-refs
+                       done
+               done
+       done &&
+       (
+               cd cloned &&
+               git fetch &&
+               git fetch
+       )
+
+'
+
 test_done
index bee3424fed770aacac6dbcdf4ba58bfd7bf5c2da..d13c806624bcc8a404be97d61500e8e1d4614c6b 100755 (executable)
@@ -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' '
index c5a2e66a09b9a871fb97142a58de51097a685ae0..e78d40242a80d9b2aa74e74d3a4aecddbb63ab99 100755 (executable)
@@ -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 &&
index 82ca3003dd2cd3c15b10280690e9ac829dc7c1eb..a696b8791b7caa44ae2bd16d6970a791f3a28d3d 100755 (executable)
@@ -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,7 +52,7 @@ test_expect_success 'fsck fails' '
 test_expect_success 'upload-pack fails due to error in rev-list' '
 
        ! echo "0032want $(git rev-parse HEAD)
-00000009done
+0034shallow $(git rev-parse HEAD^)00000009done
 0000" | git upload-pack . > /dev/null 2> output.err &&
        # pack-objects survived
        grep "Total.*, reused" output.err &&
@@ -59,6 +60,15 @@ test_expect_success 'upload-pack fails due to error 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 "bad tree object" output.err &&
+       grep "pack-objects died" output.err
+'
+
 test_expect_success 'create empty repository' '
 
        mkdir foo &&
index 04e4b7c5c2aa4f7c6922ebc5c9236962ab5d175c..0144d9e858d2d8bf1720331c52f1809ed36e81b0 100755 (executable)
@@ -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 &&
index a19d49de28c457d1a5726400d25c1f731506a05f..e71c687f2b9473842684fae1a1b4b013c721497f 100755 (executable)
@@ -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
index 7868af8f1896e621b98a2ae9b71492a68a96e584..b13aa7e89ad51566979ab674baa2ac36c7e33da6 100755 (executable)
@@ -8,6 +8,15 @@ test_description='git grep various.
 
 . ./test-lib.sh
 
+cat >hello.c <<EOF
+#include <stdio.h>
+int main(int argc, const char **argv)
+{
+       printf("Hello world.\n");
+       return 0;
+}
+EOF
+
 test_expect_success setup '
        {
                echo foo mmap bar
@@ -22,7 +31,7 @@ test_expect_success setup '
        echo zzz > z &&
        mkdir t &&
        echo test >t/t &&
-       git add file w x y z t/t &&
+       git add file w x y z t/t hello.c &&
        test_tick &&
        git commit -m initial
 '
@@ -155,6 +164,28 @@ test_expect_success 'grep -e A --and --not -e B' '
        test_cmp expected actual
 '
 
+cat >expected <<EOF
+y:y yy
+--
+z:zzz
+EOF
+
+# Create 1024 file names that sort between "y" and "z" to make sure
+# the two files are handled by different calls to an external grep.
+# This depends on MAXARGS in builtin-grep.c being 1024 or less.
+c32="0 1 2 3 4 5 6 7 8 9 a b c d e f g h i j k l m n o p q r s t u v"
+test_expect_success 'grep -C1, hunk mark between files' '
+       for a in $c32; do for b in $c32; do : >y-$a$b; done; done &&
+       git add y-?? &&
+       git grep -C1 "^[yz]" >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'grep -C1 --no-ext-grep, hunk mark between files' '
+       git grep -C1 --no-ext-grep "^[yz]" >actual &&
+       test_cmp expected actual
+'
+
 test_expect_success 'log grep setup' '
        echo a >>file &&
        test_tick &&
@@ -207,9 +238,45 @@ test_expect_success 'log grep (6)' '
 test_expect_success 'grep with CE_VALID file' '
        git update-index --assume-unchanged t/t &&
        rm t/t &&
-       test "$(git grep --no-ext-grep t)" = "t/t:test" &&
+       test "$(git grep --no-ext-grep test)" = "t/t:test" &&
        git update-index --no-assume-unchanged t/t &&
        git checkout t/t
 '
 
+cat >expected <<EOF
+hello.c=#include <stdio.h>
+hello.c:       return 0;
+EOF
+
+test_expect_success 'grep -p with userdiff' '
+       git config diff.custom.funcname "^#" &&
+       echo "hello.c diff=custom" >.gitattributes &&
+       git grep -p return >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c=int main(int argc, const char **argv)
+hello.c:       return 0;
+EOF
+
+test_expect_success 'grep -p' '
+       rm -f .gitattributes &&
+       git grep -p return >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+hello.c-#include <stdio.h>
+hello.c=int main(int argc, const char **argv)
+hello.c-{
+hello.c-       printf("Hello world.\n");
+hello.c:       return 0;
+EOF
+
+test_expect_success 'grep -p -B5' '
+       git grep -p -B5 return >actual &&
+       test_cmp expected actual
+'
+
 test_done
index 87c9b0e121db6c8ab7074e6c4968cd06d37851fe..f4aa0547501a19fe570304e830504ff984f5a9a9 100755 (executable)
@@ -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
 
index 9cca14d080e63d25f5d71179deaced8b226bba07..cb390559f947fe3cd89a99b9bd473d60c7083307 100755 (executable)
@@ -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 (file)
index 0000000..288f040
--- /dev/null
@@ -0,0 +1,2 @@
+EUC_JAPAN_NAME="»³ÅÄ ÂÀϺ"
+EUC_JAPAN_MSG="¥Ö¥ì¡¼¥à¤Î¥Æ¥¹¥È¤Ç¤¹¡£"
index 2ccfbad207c6e96b1f4f528031d9e4938d364b92..bbdefeaced4b54f98e5d9a85ddd8e0d7346fe7e3 100644 (file)
@@ -1,2 +1,2 @@
-SJIS_NAME="\84I\84r\84p\84\84P\84u\84\84\84\82\84\80\84r\84y\84\89 \84R\84y\84t\84\80\84\82\84\80\84r"
-SJIS_MSG="\84S\84u\84\83\84\84\84\80\84r\84\80\84\84\83\84\80\84\80\84q\84\8b\84u\84~\84y\84u"
+SJIS_NAME="\8eR\93\91¾\98Y"
+SJIS_MSG="\83u\83\8c\81[\83\80\82Ì\83e\83X\83g\82Å\82·\81B"
index f46cfc56d80797740c3ec15e166add052f905fcb..4d00dbea7659ee27fda283e7e45cfb2d5f6ea4d1 100644 (file)
@@ -1,2 +1,2 @@
-UTF8_NAME="Иван Петрович Сидоров"
-UTF8_MSG="Тестовое сообщение"
+UTF8_NAME="山田 太郎"
+UTF8_MSG="ブレームのテストです。"
index 9da4178c94f4bde62f69f32701b16ab3d04524e0..929499e996bc33d96ebab0b01ebcac8240e689b7 100755 (executable)
@@ -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
index 3a9e07768d8e6bebd7a4c4156ace6c5746d7d440..c0098d98084ceee4e7c8800b1f8612c06f84d268 100755 (executable)
@@ -56,7 +56,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:
@@ -86,14 +94,14 @@ test_expect_success 'migrate --minimize on old inited layout' '
                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
        '
 
similarity index 93%
rename from t/t9138-git-svn-multiple-branches.sh
rename to t/t9141-git-svn-multiple-branches.sh
index cb9a6d229d24a4f4af062642d07660c5701429f6..3cd06718eb552794e04afe0860d0fc1e187eed66 100755 (executable)
@@ -99,22 +99,22 @@ test_expect_success 'Multiple branch or tag paths require -d' '
 
 test_expect_success 'create new branches and tags' '
        ( cd git_project &&
-               git svn branch -m "New branch 1" -d project/b_one New1 ) &&
+               git svn branch -m "New branch 1" -d b_one New1 ) &&
        ( cd svn_project &&
                svn_cmd up && test -e b_one/New1/a.file ) &&
 
        ( cd git_project &&
-               git svn branch -m "New branch 2" -d project/b_two New2 ) &&
+               git svn branch -m "New branch 2" -d b_two New2 ) &&
        ( cd svn_project &&
                svn_cmd up && test -e b_two/New2/a.file ) &&
 
        ( cd git_project &&
-               git svn branch -t -m "New tag 1" -d project/tags_A Tag1 ) &&
+               git svn branch -t -m "New tag 1" -d tags_A Tag1 ) &&
        ( cd svn_project &&
                svn_cmd up && test -e tags_A/Tag1/a.file ) &&
 
        ( cd git_project &&
-               git svn tag -m "New tag 2" -d project/tags_B Tag2 ) &&
+               git svn tag -m "New tag 2" -d tags_B Tag2 ) &&
        ( cd svn_project &&
                svn_cmd up && test -e tags_B/Tag2/a.file )
 '
diff --git a/t/t9142-git-svn-shallow-clone.sh b/t/t9142-git-svn-shallow-clone.sh
new file mode 100755 (executable)
index 0000000..1236acc
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Eric Wong
+#
+
+test_description='git svn shallow clone'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup test repository' '
+       svn_cmd mkdir -m "create standard layout" \
+         "$svnrepo"/trunk "$svnrepo"/branches "$svnrepo"/tags &&
+       svn_cmd cp -m "branch off trunk" \
+         "$svnrepo"/trunk "$svnrepo"/branches/a &&
+       svn_cmd co "$svnrepo"/branches/a &&
+       (
+               cd a &&
+               > foo &&
+               svn_cmd add foo &&
+               svn_cmd commit -m "add foo"
+       )
+'
+
+start_httpd
+
+test_expect_success 'clone trunk with "-r HEAD"' '
+       git svn clone -r HEAD "$svnrepo/trunk" g &&
+       ( 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 (executable)
index 0000000..f2ba2d1
--- /dev/null
@@ -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/git-svn/unhandled.log tmp
+       '
+
+test_expect_success 'create leftover index' '> .git/svn/git-svn/index'
+
+test_expect_success 'git svn gc runs' 'git svn gc'
+
+test_expect_success 'git svn index removed' '! test -f .git/svn/git-svn/index'
+
+if perl -MCompress::Zlib -e 0 2>/dev/null
+then
+       test_expect_success 'git svn gc produces a valid gzip file' '
+                gunzip .git/svn/git-svn/unhandled.log.gz
+               '
+else
+       say "Perl Compress::Zlib unavailable, skipping gunzip test"
+fi
+
+test_expect_success 'git svn gc does not change unhandled.log files' '
+        test_cmp .git/svn/git-svn/unhandled.log tmp/unhandled.log
+       '
+
+test_done
index ef1f8d22f67187089ff88811457d8e26cf354047..fc3795dc98803bd98e2ebd6f38a249c331038d54 100755 (executable)
@@ -288,6 +288,27 @@ test_expect_success 'check files before directories' '
 
 '
 
+test_expect_success 're-commit a removed filename which remains in CVS attic' '
+
+    (cd "$CVSWORK" &&
+     echo >attic_gremlin &&
+     cvs -Q add attic_gremlin &&
+     cvs -Q ci -m "added attic_gremlin" &&
+     rm attic_gremlin &&
+     cvs -Q rm attic_gremlin &&
+     cvs -Q ci -m "removed attic_gremlin") &&
+
+    echo > attic_gremlin &&
+    git add attic_gremlin &&
+    git commit -m "Added attic_gremlin" &&
+       git cvsexportcommit -w "$CVSWORK" -c HEAD &&
+    (cd "$CVSWORK"; cvs -Q update -d) &&
+    test -f "$CVSWORK/attic_gremlin"
+'
+
+# the state of the CVS sandbox may be indeterminate for ' space'
+# after this test on some platforms / with some versions of CVS
+# consider adding new tests above this point
 test_expect_success 'commit a file with leading spaces in the name' '
 
        echo space > " space" &&
@@ -295,7 +316,7 @@ test_expect_success 'commit a file with leading spaces in the name' '
        git commit -m "Add a file with a leading space" &&
        id=$(git rev-parse HEAD) &&
        git cvsexportcommit -w "$CVSWORK" -c $id &&
-       check_entries "$CVSWORK" " space/1.1/|DS/1.1/|release-notes/1.2/" &&
+       check_entries "$CVSWORK" " space/1.1/|DS/1.1/|attic_gremlin/1.3/|release-notes/1.2/" &&
        test_cmp "$CVSWORK/ space" " space"
 
 '
@@ -317,22 +338,4 @@ test_expect_success 'use the same checkout for Git and CVS' '
 
 '
 
-test_expect_success 're-commit a removed filename which remains in CVS attic' '
-
-    (cd "$CVSWORK" &&
-     echo >attic_gremlin &&
-     cvs -Q add attic_gremlin &&
-     cvs -Q ci -m "added attic_gremlin" &&
-     rm attic_gremlin &&
-     cvs -Q rm attic_gremlin &&
-     cvs -Q ci -m "removed attic_gremlin") &&
-
-    echo > attic_gremlin &&
-    git add attic_gremlin &&
-    git commit -m "Added attic_gremlin" &&
-       git cvsexportcommit -w "$CVSWORK" -c HEAD &&
-    (cd "$CVSWORK"; cvs -Q update -d) &&
-    test -f "$CVSWORK/attic_gremlin"
-'
-
 test_done
index 8c8a9e63c26c594a837d266bff56f5f85cb376c8..356964e53a1acba1558881865fd99acdee48a17f 100755 (executable)
@@ -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'
index d539619e89c5263e3b8339c408f1c0dcd867457b..627518108acfbab990fc259412e395150c1fb5b1 100755 (executable)
@@ -660,6 +660,7 @@ cat >>gitweb_config.perl <<EOF
 
 \$feature{'blame'}{'override'} = 1;
 \$feature{'snapshot'}{'override'} = 1;
+\$feature{'avatar'}{'override'} = 1;
 EOF
 
 test_expect_success \
@@ -671,6 +672,7 @@ test_expect_success \
        'config override: tree view, features disabled in repo config' \
        'git config gitweb.blame no &&
         git config gitweb.snapshot none &&
+        git config gitweb.avatar gravatar &&
         gitweb_run "p=.git;a=tree"'
 test_debug 'cat gitweb.log'
 
index 4322a0c1ed6d792cbba4b52a7ba8bad74986bbad..363345faef7b1eb209c548914b94460d9475cb13 100755 (executable)
@@ -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 (executable)
index 0000000..3afaf56
--- /dev/null
@@ -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 (file)
index 0000000..562b12e
--- /dev/null
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9601/cvsroot/CVSROOT/.gitignore b/t/t9601/cvsroot/CVSROOT/.gitignore
new file mode 100644 (file)
index 0000000..3bb9b34
--- /dev/null
@@ -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 (file)
index 0000000..5f83072
--- /dev/null
@@ -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 (file)
index 0000000..55e1b0c
--- /dev/null
@@ -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 (file)
index 0000000..e5830ae
--- /dev/null
@@ -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 (file)
index 0000000..bbcfe44
--- /dev/null
@@ -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 (file)
index 0000000..c5dd82b
--- /dev/null
@@ -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 (file)
index 0000000..d1f3f1b
--- /dev/null
@@ -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 (executable)
index 0000000..67878b2
--- /dev/null
@@ -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 (file)
index 0000000..c231e0f
--- /dev/null
@@ -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 (file)
index 0000000..562b12e
--- /dev/null
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9602/cvsroot/CVSROOT/.gitignore b/t/t9602/cvsroot/CVSROOT/.gitignore
new file mode 100644 (file)
index 0000000..3bb9b34
--- /dev/null
@@ -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 (file)
index 0000000..3b68382
--- /dev/null
@@ -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 (file)
index 0000000..b7fdccd
--- /dev/null
@@ -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 (file)
index 0000000..472b7b2
--- /dev/null
@@ -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 (file)
index 0000000..fe6efa4
--- /dev/null
@@ -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 (file)
index 0000000..34c9789
--- /dev/null
@@ -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 (file)
index 0000000..018f7f8
--- /dev/null
@@ -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 (file)
index 0000000..d13242c
--- /dev/null
@@ -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 (file)
index 0000000..88e4567
--- /dev/null
@@ -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 (executable)
index 0000000..958bdce
--- /dev/null
@@ -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 (file)
index 0000000..562b12e
--- /dev/null
@@ -0,0 +1 @@
+* -whitespace
diff --git a/t/t9603/cvsroot/CVSROOT/.gitignore b/t/t9603/cvsroot/CVSROOT/.gitignore
new file mode 100644 (file)
index 0000000..3bb9b34
--- /dev/null
@@ -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 (file)
index 0000000..ba8fd5a
--- /dev/null
@@ -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 (file)
index 0000000..d268855
--- /dev/null
@@ -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
+@
index 5fdc5d94a20cfcf231bf23590784c5146a3e44bc..a5b8d03db0fc88c42e38cdde13a6cd2b14c06581 100644 (file)
@@ -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"
index 9b98d07c786b36f005f4b3ffd51926f3c1a343dd..80daba980ecd85852ee3a23c155602e4cd1ba07a 100644 (file)
@@ -32,7 +32,7 @@ int main(int ac, char **av)
                        if (sz == 0)
                                break;
                        if (sz < 0)
-                               die("test-sha1: %s", strerror(errno));
+                               die_errno("test-sha1");
                        this_sz += sz;
                        cp += sz;
                        room -= sz;
index 802ce7f233116cc52367b965e9e0d97bd8553535..8a42e76b3eafcb48b0904ab6f66b525624609477 100644 (file)
@@ -158,7 +158,7 @@ static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
 
        strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
        if (!mkdtemp(temp_dir.buf))
-               die ("Could not make temporary directory");
+               die_errno ("Could not make temporary directory");
        temp_dir_len = temp_dir.len;
 
        strbuf_addstr(&buf, rsync_url(transport->url));
@@ -321,7 +321,7 @@ static int rsync_transport_push(struct transport *transport,
 
        strbuf_addstr(&temp_dir, git_path("rsync-refs-XXXXXX"));
        if (!mkdtemp(temp_dir.buf))
-               die ("Could not make temporary directory");
+               die_errno ("Could not make temporary directory");
        strbuf_addch(&temp_dir, '/');
 
        if (flags & TRANSPORT_PUSH_ALL) {
@@ -705,19 +705,30 @@ static void update_tracking_ref(struct remote *remote, struct ref *ref, int verb
 
 #define SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 
-static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg)
+static void print_ref_status(char flag, const char *summary, struct ref *to, struct ref *from, const char *msg, int porcelain)
 {
-       fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
-       if (from)
-               fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
-       else
-               fputs(prettify_refname(to->name), stderr);
-       if (msg) {
-               fputs(" (", stderr);
-               fputs(msg, stderr);
-               fputc(')', stderr);
+       if (porcelain) {
+               if (from)
+                       fprintf(stdout, "%c\t%s:%s\t", flag, from->name, to->name);
+               else
+                       fprintf(stdout, "%c\t:%s\t", flag, to->name);
+               if (msg)
+                       fprintf(stdout, "%s (%s)\n", summary, msg);
+               else
+                       fprintf(stdout, "%s\n", summary);
+       } else {
+               fprintf(stderr, " %c %-*s ", flag, SUMMARY_WIDTH, summary);
+               if (from)
+                       fprintf(stderr, "%s -> %s", prettify_refname(from->name), prettify_refname(to->name));
+               else
+                       fputs(prettify_refname(to->name), stderr);
+               if (msg) {
+                       fputs(" (", stderr);
+                       fputs(msg, stderr);
+                       fputc(')', stderr);
+               }
+               fputc('\n', stderr);
        }
-       fputc('\n', stderr);
 }
 
 static const char *status_abbrev(unsigned char sha1[20])
@@ -725,15 +736,15 @@ static const char *status_abbrev(unsigned char sha1[20])
        return find_unique_abbrev(sha1, DEFAULT_ABBREV);
 }
 
-static void print_ok_ref_status(struct ref *ref)
+static void print_ok_ref_status(struct ref *ref, int porcelain)
 {
        if (ref->deletion)
-               print_ref_status('-', "[deleted]", ref, NULL, NULL);
+               print_ref_status('-', "[deleted]", ref, NULL, NULL, porcelain);
        else if (is_null_sha1(ref->old_sha1))
                print_ref_status('*',
                        (!prefixcmp(ref->name, "refs/tags/") ? "[new tag]" :
-                         "[new branch]"),
-                       ref, ref->peer_ref, NULL);
+                       "[new branch]"),
+                       ref, ref->peer_ref, NULL, porcelain);
        else {
                char quickref[84];
                char type;
@@ -751,50 +762,51 @@ static void print_ok_ref_status(struct ref *ref)
                }
                strcat(quickref, status_abbrev(ref->new_sha1));
 
-               print_ref_status(type, quickref, ref, ref->peer_ref, msg);
+               print_ref_status(type, quickref, ref, ref->peer_ref, msg, porcelain);
        }
 }
 
-static int print_one_push_status(struct ref *ref, const char *dest, int count)
+static int print_one_push_status(struct ref *ref, const char *dest, int count, int porcelain)
 {
        if (!count)
                fprintf(stderr, "To %s\n", dest);
 
        switch(ref->status) {
        case REF_STATUS_NONE:
-               print_ref_status('X', "[no match]", ref, NULL, NULL);
+               print_ref_status('X', "[no match]", ref, NULL, NULL, porcelain);
                break;
        case REF_STATUS_REJECT_NODELETE:
                print_ref_status('!', "[rejected]", ref, NULL,
-                               "remote does not support deleting refs");
+                                                "remote does not support deleting refs", porcelain);
                break;
        case REF_STATUS_UPTODATE:
                print_ref_status('=', "[up to date]", ref,
-                               ref->peer_ref, NULL);
+                                                ref->peer_ref, NULL, porcelain);
                break;
        case REF_STATUS_REJECT_NONFASTFORWARD:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                               "non-fast forward");
+                                                "non-fast forward", porcelain);
                break;
        case REF_STATUS_REMOTE_REJECT:
                print_ref_status('!', "[remote rejected]", ref,
-                               ref->deletion ? NULL : ref->peer_ref,
-                               ref->remote_status);
+                                                ref->deletion ? NULL : ref->peer_ref,
+                                                ref->remote_status, porcelain);
                break;
        case REF_STATUS_EXPECTING_REPORT:
                print_ref_status('!', "[remote failure]", ref,
-                               ref->deletion ? NULL : ref->peer_ref,
-                               "remote failed to report status");
+                                                ref->deletion ? NULL : ref->peer_ref,
+                                                "remote failed to report status", porcelain);
                break;
        case REF_STATUS_OK:
-               print_ok_ref_status(ref);
+               print_ok_ref_status(ref, porcelain);
                break;
        }
 
        return 1;
 }
 
-static void print_push_status(const char *dest, struct ref *refs, int verbose)
+static void print_push_status(const char *dest, struct ref *refs,
+                                                         int verbose, int porcelain)
 {
        struct ref *ref;
        int n = 0;
@@ -802,18 +814,18 @@ static void print_push_status(const char *dest, struct ref *refs, int verbose)
        if (verbose) {
                for (ref = refs; ref; ref = ref->next)
                        if (ref->status == REF_STATUS_UPTODATE)
-                               n += print_one_push_status(ref, dest, n);
+                               n += print_one_push_status(ref, dest, n, porcelain);
        }
 
        for (ref = refs; ref; ref = ref->next)
                if (ref->status == REF_STATUS_OK)
-                       n += print_one_push_status(ref, dest, n);
+                       n += print_one_push_status(ref, dest, n, porcelain);
 
        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);
+                       n += print_one_push_status(ref, dest, n, porcelain);
        }
 }
 
@@ -983,6 +995,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 porcelain = flags & TRANSPORT_PUSH_PORCELAIN;
                int ret;
 
                if (flags & TRANSPORT_PUSH_ALL)
@@ -997,7 +1010,7 @@ int transport_push(struct transport *transport,
 
                ret = transport->push_refs(transport, remote_refs, flags);
 
-               print_push_status(transport->url, remote_refs, verbose);
+               print_push_status(transport->url, remote_refs, verbose | porcelain, porcelain);
 
                if (!(flags & TRANSPORT_PUSH_DRY_RUN)) {
                        struct ref *ref;
index 27bfc528acb5b51006c7d61db0d1d7910adaf31c..51b539778c2f63591c4f032e311372b2e9975a37 100644 (file)
@@ -35,6 +35,7 @@ struct transport {
 #define TRANSPORT_PUSH_DRY_RUN 4
 #define TRANSPORT_PUSH_MIRROR 8
 #define TRANSPORT_PUSH_VERBOSE 16
+#define TRANSPORT_PUSH_PORCELAIN 32
 
 /* Returns a transport suitable for the url */
 struct transport *transport_get(struct remote *, const char *);
index 75cd2f1a6adf3ea773a6acc3f1e7f122e30676e5..ac9cbf7cd8ed1367151de0e8b96668f498b7f1a1 100644 (file)
@@ -17,7 +17,7 @@ static char *create_temp_file(unsigned char *sha1)
        strcpy(path, ".merge_file_XXXXXX");
        fd = xmkstemp(path);
        if (write_in_full(fd, buf, size) != size)
-               die("unable to write temp-file");
+               die_errno("unable to write temp-file");
        close(fd);
        return path;
 }
index 05d0bb1f85467a275bb78173d3a045940262abe7..720f7a161651126872b7357173c10d69d1db7636 100644 (file)
@@ -128,7 +128,7 @@ static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_o
 
 static int unpack_index_entry(struct cache_entry *ce, struct unpack_trees_options *o)
 {
-       struct cache_entry *src[5] = { ce, };
+       struct cache_entry *src[5] = { ce, NULL, };
 
        o->pos++;
        if (ce_stage(ce)) {
@@ -551,7 +551,7 @@ static int verify_clean_subdirectory(struct cache_entry *ce, const char *action,
        memset(&d, 0, sizeof(d));
        if (o->dir)
                d.exclude_per_dir = o->dir->exclude_per_dir;
-       i = read_directory(&d, ce->name, pathbuf, namelen+1, NULL);
+       i = read_directory(&d, pathbuf, namelen+1, NULL);
        if (i)
                return o->gently ? -1 :
                        error(ERRORMSG(o, not_uptodate_dir), ce->name);
@@ -999,12 +999,12 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
                return error("Cannot do a oneway merge of %d trees",
                             o->merge_size);
 
-       if (!a)
+       if (!a || a == o->df_conflict_entry)
                return deleted_entry(old, old, o);
 
        if (old && same(old, a)) {
                int update = 0;
-               if (o->reset) {
+               if (o->reset && !ce_uptodate(old)) {
                        struct stat st;
                        if (lstat(old->name, &st) ||
                            ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID))
index 1e0e2325f150e943dd22b16c1392c136570732bc..d19df44f4070ca1bda29382e56fbced53aacd994 100644 (file)
@@ -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;
index 841ebb534ac3393f5ee80692f1402c82a1b4a5f6..f7d308a411d22cf350a6ac4512ee2ebb195f87d7 100644 (file)
@@ -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/usage.c b/usage.c
index 820d09f92b03b6cc96fbe9a954c37fd2d5e5a417..b6aea45280bf2aa4c06a0130f912a61377165016 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -60,6 +60,34 @@ void die(const char *err, ...)
        va_end(params);
 }
 
+void die_errno(const char *fmt, ...)
+{
+       va_list params;
+       char fmt_with_err[1024];
+       char str_error[256], *err;
+       int i, j;
+
+       err = strerror(errno);
+       for (i = j = 0; err[i] && j < sizeof(str_error) - 1; ) {
+               if ((str_error[j++] = err[i++]) != '%')
+                       continue;
+               if (j < sizeof(str_error) - 1) {
+                       str_error[j++] = '%';
+               } else {
+                       /* No room to double the '%', so we overwrite it with
+                        * '\0' below */
+                       j--;
+                       break;
+               }
+       }
+       str_error[j] = 0;
+       snprintf(fmt_with_err, sizeof(fmt_with_err), "%s: %s", fmt, str_error);
+
+       va_start(params, fmt);
+       die_routine(fmt_with_err, params);
+       va_end(params);
+}
+
 int error(const char *err, ...)
 {
        va_list params;
index e57630e983488e5c0222dc47a5e32d6efb184762..11d9052ed86ab17b56ab86ef21b7c98095e410dc 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -245,7 +245,7 @@ void walker_targets_free(int targets, char **target, const char **write_ref)
 {
        while (targets--) {
                free(target[targets]);
-               if (write_ref && write_ref[targets])
+               if (write_ref)
                        free((char *) write_ref[targets]);
        }
 }
index 7eb3218ee995991881252e4bb599452d7214ae4f..c9be1400c005e25b003acecc0cb037dd2f07e56f 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -96,7 +96,7 @@ void *xmmap(void *start, size_t length,
                release_pack_memory(length, fd);
                ret = mmap(start, length, prot, flags, fd, offset);
                if (ret == MAP_FAILED)
-                       die("Out of memory? mmap failed: %s", strerror(errno));
+                       die_errno("Out of memory? mmap failed");
        }
        return ret;
 }
@@ -175,7 +175,7 @@ int xdup(int fd)
 {
        int ret = dup(fd);
        if (ret < 0)
-               die("dup failed: %s", strerror(errno));
+               die_errno("dup failed");
        return ret;
 }
 
@@ -183,7 +183,7 @@ FILE *xfdopen(int fd, const char *mode)
 {
        FILE *stream = fdopen(fd, mode);
        if (stream == NULL)
-               die("Out of memory? fdopen failed: %s", strerror(errno));
+               die_errno("Out of memory? fdopen failed");
        return stream;
 }
 
@@ -193,7 +193,7 @@ int xmkstemp(char *template)
 
        fd = mkstemp(template);
        if (fd < 0)
-               die("Unable to create temporary file: %s", strerror(errno));
+               die_errno("Unable to create temporary file");
        return fd;
 }
 
index 4c29255df1b637f93ab3d59e0dcab1fa3b40e10b..d45b536021e15c5a674f7969be39f238194bef99 100644 (file)
@@ -41,14 +41,14 @@ void maybe_flush_or_die(FILE *f, const char *desc)
                 */
                if (errno == EPIPE || errno == EINVAL)
                        exit(0);
-               die("write failure on %s: %s", desc, strerror(errno));
+               die_errno("write failure on '%s'", desc);
        }
 }
 
 void fsync_or_die(int fd, const char *msg)
 {
        if (fsync(fd) < 0) {
-               die("%s: fsync error (%s)", msg, strerror(errno));
+               die_errno("fsync error on '%s'", msg);
        }
 }
 
@@ -57,7 +57,7 @@ void write_or_die(int fd, const void *buf, size_t count)
        if (write_in_full(fd, buf, count) < 0) {
                if (errno == EPIPE)
                        exit(0);
-               die("write error (%s)", strerror(errno));
+               die_errno("write error");
        }
 }
 
diff --git a/ws.c b/ws.c
index 819c797cf6f7a265c01f00033fa48fdab94a0943..59d0883c1f3a46edcbb2e45b172c9eac3b9d5e35 100644 (file)
--- a/ws.c
+++ b/ws.c
@@ -261,12 +261,11 @@ 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) &&
-           (2 <= len && isspace(src[len-2]))) {
-               if (src[len - 1] == '\n') {
+       if (ws_rule & WS_TRAILING_SPACE) {
+               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--;
                        }
index 0ca4b13c29ee6a8cebc1db4ec59f543aef299484..47735d8129fd3632b85d54b451e30752f251a949 100644 (file)
@@ -255,7 +255,7 @@ static void wt_status_print_untracked(struct wt_status *s)
                        DIR_SHOW_OTHER_DIRECTORIES | DIR_HIDE_EMPTY_DIRECTORIES;
        setup_standard_excludes(&dir);
 
-       read_directory(&dir, ".", "", 0, NULL);
+       fill_directory(&dir, NULL);
        for(i = 0; i < dir.nr; i++) {
                struct dir_entry *ent = dir.entries[i];
                if (!cache_name_is_other(ent->name, ent->len))
index b9b0db8d86615d6ca1046b932772f3d9750a8062..01f14fb50f7cf1387898a0c8db44f966ce07b720 100644 (file)
@@ -309,6 +309,21 @@ void xdiff_set_find_func(xdemitconf_t *xecfg, const char *value, int cflags)
        }
 }
 
+void xdiff_clear_find_func(xdemitconf_t *xecfg)
+{
+       if (xecfg->find_func) {
+               int i;
+               struct ff_regs *regs = xecfg->find_func_priv;
+
+               for (i = 0; i < regs->nr; i++)
+                       regfree(&regs->array[i].re);
+               free(regs->array);
+               free(regs);
+               xecfg->find_func = NULL;
+               xecfg->find_func_priv = NULL;
+       }
+}
+
 int git_xmerge_style = -1;
 
 int git_xmerge_config(const char *var, const char *value, void *cb)
index 7352b9a9c204c2b1d4ca9df5ce040fe22d6f521c..55572c39a10dee336355f816b324946fc087d6e7 100644 (file)
@@ -21,6 +21,7 @@ int read_mmfile(mmfile_t *ptr, const char *filename);
 int buffer_is_binary(const char *ptr, unsigned long size);
 
 extern void xdiff_set_find_func(xdemitconf_t *xecfg, const char *line, int cflags);
+extern void xdiff_clear_find_func(xdemitconf_t *xecfg);
 extern int git_xmerge_config(const char *var, const char *value, void *cb);
 extern int git_xmerge_style;
 
index 1ebab687f77218d1520a4527214170eeab176d31..da67c04357dfe4d3283c589f5d47be9c5f2b7fcf 100644 (file)
@@ -26,7 +26,7 @@
 
 #define XDL_MAX_COST_MIN 256
 #define XDL_HEUR_MIN_COST 256
-#define XDL_LINE_MAX (long)((1UL << (8 * sizeof(long) - 1)) - 1)
+#define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)
 #define XDL_SNAKE_CNT 20
 #define XDL_K_HEUR 4