Code

Merge branch 'tr/http-push-ref-status'
authorJunio C Hamano <gitster@pobox.com>
Wed, 20 Jan 2010 22:39:48 +0000 (14:39 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 Jan 2010 22:39:48 +0000 (14:39 -0800)
* tr/http-push-ref-status:
  transport-helper.c::push_refs(): emit "no refs" error message
  transport-helper.c::push_refs(): ignore helper-reported status if ref is not to be pushed
  transport.c::transport_push(): make ref status affect return value
  refactor ref status logic for pushing
  t5541-http-push.sh: add test for unmatched, non-fast-forwarded refs
  t5541-http-push.sh: add tests for non-fast-forward pushes

Conflicts:
transport-helper.c

174 files changed:
.gitattributes
.gitignore
COPYING
Documentation/Makefile
Documentation/RelNotes-1.6.5.8.txt [new file with mode: 0644]
Documentation/RelNotes-1.6.6.1.txt
Documentation/RelNotes-1.7.0.txt
Documentation/SubmittingPatches
Documentation/blame-options.txt
Documentation/config.txt
Documentation/date-formats.txt [new file with mode: 0644]
Documentation/git-add.txt
Documentation/git-blame.txt
Documentation/git-clone.txt
Documentation/git-commit-tree.txt
Documentation/git-commit.txt
Documentation/git-config.txt
Documentation/git-cvsserver.txt
Documentation/git-fast-import.txt
Documentation/git-http-backend.txt
Documentation/git-ls-files.txt
Documentation/git-merge.txt
Documentation/git-push.txt
Documentation/git-read-tree.txt
Documentation/git-remote-helpers.txt
Documentation/git-reset.txt
Documentation/git-rev-parse.txt
Documentation/git-rm.txt
Documentation/git-update-index.txt
Documentation/git.txt
Documentation/technical/api-directory-listing.txt
INSTALL
Makefile
archive.c
base85.c
bisect.c
bisect.h
blob.c
blob.h
builtin-apply.c
builtin-archive.c
builtin-branch.c
builtin-checkout.c
builtin-clean.c
builtin-clone.c
builtin-commit.c
builtin-config.c
builtin-count-objects.c
builtin-gc.c
builtin-grep.c
builtin-help.c
builtin-ls-files.c
builtin-merge.c
builtin-mv.c
builtin-pack-objects.c
builtin-push.c
builtin-read-tree.c
builtin-remote.c
builtin-rerere.c
builtin-reset.c
builtin-rev-list.c
builtin-rev-parse.c
builtin-revert.c
builtin-tag.c
builtin-update-index.c
cache.h
commit.h
compat/mingw.c
compat/mingw.h
compat/msvc.h
compat/win32/pthread.c [new file with mode: 0644]
compat/win32/pthread.h [new file with mode: 0644]
config.c
contrib/completion/git-completion.bash
contrib/hg-to-git/hg-to-git.py
convert.c
daemon.c
date.c
diff-lib.c
diff.c
dir.c
dir.h
editor.c
entry.c
environment.c
fast-import.c
git-am.sh
git-compat-util.h
git-cvsserver.perl
git-filter-branch.sh
git-merge-octopus.sh
git-rebase--interactive.sh
git-rebase.sh
git-sh-setup.sh
git-stash.sh
http-backend.c
http.c
http.h
imap-send.c
ll-merge.c
lockfile.c
mailmap.c
mailmap.h
object.c
object.h
pager.c
parse-options.c
parse-options.h
pretty.c
quote.c
quote.h
read-cache.c
remote-curl.c
rerere.c
rerere.h
run-command.c
run-command.h
setup.c
sha1_file.c
sha1_name.c
strbuf.c
strbuf.h
string-list.h
submodule.c
symlinks.c
t/README
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/t0021-conversion.sh
t/t1011-read-tree-sparse-checkout.sh [new file with mode: 0755]
t/t1300-repo-config.sh
t/t1506-rev-parse-diagnosis.sh [new file with mode: 0755]
t/t2012-checkout-last.sh
t/t2104-update-index-gitfile.sh [new file with mode: 0755]
t/t2104-update-index-skip-worktree.sh [new file with mode: 0755]
t/t3001-ls-files-others-exclude.sh
t/t3415-rebase-onto-threedots.sh [new file with mode: 0755]
t/t4030-diff-textconv.sh
t/t4031-diff-rewrite-binary.sh
t/t4200-rerere.sh
t/t5403-post-checkout-hook.sh
t/t5505-remote.sh
t/t5516-fetch-push.sh
t/t5551-http-fetch.sh
t/t5560-http-backend-noserver.sh [new file with mode: 0755]
t/t5560-http-backend.sh [deleted file]
t/t5561-http-backend.sh [new file with mode: 0755]
t/t556x_common [new file with mode: 0755]
t/t5702-clone-options.sh
t/t6030-bisect-porcelain.sh
t/t7002-grep.sh
t/t7011-skip-worktree-reading.sh [new file with mode: 0755]
t/t7012-skip-worktree-writing.sh [new file with mode: 0755]
t/t7102-reset.sh
t/t7103-reset-bare.sh
t/t7110-reset-merge.sh [new file with mode: 0755]
t/t7111-reset-table.sh [new file with mode: 0755]
t/t7201-co.sh
t/t7300-clean.sh
t/t7400-submodule-basic.sh
t/t7501-commit.sh
t/t7502-commit.sh
t/t7602-merge-octopus-many.sh
t/t9300-fast-import.sh
t/test-lib.sh
test-index-version.c [new file with mode: 0644]
transport-helper.c
transport.c
transport.h
unpack-trees.c
unpack-trees.h
utf8.c
utf8.h
wrap-for-bin.sh [new file with mode: 0644]

index 0636deea9357d2f1e9331119f02fb75fb6b15393..5e98806c6cc246acef5f539ae191710a0c06ad3f 100644 (file)
@@ -1,2 +1,3 @@
 * whitespace=!indent,trail,space
 *.[ch] whitespace=indent,trail,space
+*.sh whitespace=indent,trail,space
index ac02a580daf07150b03194a5a0de522ac1e3415d..83cf1b7532f6cf35c1fefdedb7372062a10b468f 100644 (file)
@@ -2,6 +2,7 @@
 /GIT-CFLAGS
 /GIT-GUI-VARS
 /GIT-VERSION-FILE
+/bin-wrappers/
 /git
 /git-add
 /git-add--interactive
 /git-relink
 /git-remote
 /git-remote-curl
+/git-remote-http
+/git-remote-https
+/git-remote-ftp
+/git-remote-ftps
 /git-repack
 /git-replace
 /git-repo-config
 /test-delta
 /test-dump-cache-tree
 /test-genrandom
+/test-index-version
 /test-match-trees
 /test-parse-options
 /test-path-utils
diff --git a/COPYING b/COPYING
index 6ff87c4664981e4397625791c8ea3bbb5f2279a3..536e55524db72bd2acf175208aef4f3dfc148d42 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -22,8 +22,8 @@
                    GNU GENERAL PUBLIC LICENSE
                       Version 2, June 1991
 
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
                      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
@@ -36,7 +36,7 @@ software--to make sure the software is free for all its users.  This
 General Public License applies to most of the Free Software
 Foundation's software and to any other program whose authors commit to
 using it.  (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.)  You can apply it to
+the GNU Lesser General Public License instead.)  You can apply it to
 your programs, too.
 
   When we speak of free software, we are referring to freedom, not
@@ -76,7 +76,7 @@ patent must be licensed for everyone's free use or not licensed at all.
 
   The precise terms and conditions for copying, distribution and
 modification follow.
-\f
+
                    GNU GENERAL PUBLIC LICENSE
    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 
@@ -131,7 +131,7 @@ above, provided that you also meet all of these conditions:
     License.  (Exception: if the Program itself is interactive but
     does not normally print such an announcement, your work based on
     the Program is not required to print an announcement.)
-\f
+
 These requirements apply to the modified work as a whole.  If
 identifiable sections of that work are not derived from the Program,
 and can be reasonably considered independent and separate works in
@@ -189,7 +189,7 @@ access to copy from a designated place, then offering equivalent
 access to copy the source code from the same place counts as
 distribution of the source code, even though third parties are not
 compelled to copy the source along with the object code.
-\f
+
   4. You may not copy, modify, sublicense, or distribute the Program
 except as expressly provided under this License.  Any attempt
 otherwise to copy, modify, sublicense or distribute the Program is
@@ -246,7 +246,7 @@ impose that choice.
 
 This section is intended to make thoroughly clear what is believed to
 be a consequence of the rest of this License.
-\f
+
   8. If the distribution and/or use of the Program is restricted in
 certain countries either by patents or by copyrighted interfaces, the
 original copyright holder who places the Program under this License
@@ -299,7 +299,7 @@ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGES.
 
                     END OF TERMS AND CONDITIONS
-\f
+
            How to Apply These Terms to Your New Programs
 
   If you develop a new program, and you want it to be of the greatest
@@ -324,10 +324,9 @@ the "copyright" line and a pointer to where the full notice is found.
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
 
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 
 Also add information on how to contact you by electronic and paper mail.
 
@@ -357,5 +356,5 @@ necessary.  Here is a sample; alter the names:
 This General Public License does not permit incorporating your program into
 proprietary programs.  If your program is a subroutine library, you may
 consider it more useful to permit linking proprietary applications with the
-library.  If this is what you want to do, use the GNU Library General
+library.  If this is what you want to do, use the GNU Lesser General
 Public License instead of this License.
index 4797b2dc3522ccd2050ddefe990268fddb6b8a47..8a8a3954dc45723f7380b59dadbb7e412198d672 100644 (file)
@@ -204,7 +204,7 @@ install-pdf: pdf
 install-html: html
        '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir)
 
-../GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
+../GIT-VERSION-FILE: FORCE
        $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) GIT-VERSION-FILE
 
 -include ../GIT-VERSION-FILE
@@ -337,4 +337,4 @@ quick-install-man:
 quick-install-html:
        '$(SHELL_PATH_SQ)' ./install-doc-quick.sh $(HTML_REF) $(DESTDIR)$(htmldir)
 
-.PHONY: .FORCE-GIT-VERSION-FILE
+.PHONY: FORCE
diff --git a/Documentation/RelNotes-1.6.5.8.txt b/Documentation/RelNotes-1.6.5.8.txt
new file mode 100644 (file)
index 0000000..8b24beb
--- /dev/null
@@ -0,0 +1,28 @@
+Git v1.6.5.8 Release Notes
+==========================
+
+Fixes since v1.6.5.7
+--------------------
+
+* "git count-objects" did not handle packfiles that are bigger than 4G on
+  platforms with 32-bit off_t.
+
+* "git rebase -i" did not abort cleanly if it failed to launch the editor.
+
+* "git blame" did not work well when commit lacked the author name.
+
+* "git fast-import" choked when handling a tag that points at an object
+  that is not a commit.
+
+* "git reset --hard" did not work correctly when GIT_WORK_TREE environment
+  variable is used to point at the root of the true work tree.
+
+* "git grep" fed a buffer that is not NUL-terminated to underlying
+  regexec().
+
+* "git checkout -m other" while on a branch that does not have any commit
+  segfaulted, instead of failing.
+
+* "git branch -a other" should have diagnosed the command as an error.
+
+Other minor documentation updates are also included.
index 4c88bebb902f9efc03767e2d59ed51c2650a8f4d..f1d0a4ae2d63e258cea94f8bca09d41121f1165e 100644 (file)
@@ -4,12 +4,34 @@ Git v1.6.6.1 Release Notes
 Fixes since v1.6.6
 ------------------
 
+ * "git blame" did not work well when commit lacked the author name.
+
+ * "git branch -a name" wasn't diagnosed as an error.
+
+ * "git count-objects" did not handle packfiles that are bigger than 4G on
+   platforms with 32-bit off_t.
+
+ * "git checkout -m other" while on a branch that does not have any commit
+   segfaulted, instead of failing.
+
+ * "git fast-import" choked when fed a tag that do not point at a
+   commit.
+
+ * "git grep" finding from work tree files could have fed garbage to
+   the underlying regexec(3).
+
+ * "git grep -L" didn't show empty files (they should never match, and
+   they should always appear in -L output as unmatching).
+
+ * "git rebase -i" did not abort cleanly if it failed to launch the editor.
+
+ * "git reset --hard" did not work correctly when GIT_WORK_TREE environment
+   variable is used to point at the root of the true work tree.
+
  * http-backend was not listed in the command list in the documentation.
 
-Other minor documentation updates are included.
+ * Building on FreeBSD (both 7 and 8) needs OLD_ICONV set in the Makefile
+
+ * "git checkout -m some-branch" while on an unborn branch crashed.
 
---
-exec >/var/tmp/1
-O=v1.6.6-4-gd828fdb
-echo O=$(git describe maint)
-git shortlog --no-merges $O..maint
+Other minor documentation updates are included.
index d519d0612f0e6ad2d47f2ebc6879c193516eb624..7a49b475da70a0daf043eda389848f0057e22e5f 100644 (file)
@@ -44,12 +44,70 @@ Updates since v1.6.6
 
 (subsystems)
 
+ * "git fast-import" updates; adds "option" and "feature" to detect the
+   mismatch between fast-import and the frontends that produce the input
+   stream.
+
 (portability)
 
+ * Some more MSVC portability patches for msysgit port.
+
+ * Minimum Pthreads emulation for msysgit port.
+
 (performance)
 
+ * More performance improvement patches for msysgit port.
+
 (usability, bells and whistles)
 
+ * More commands learned "--quiet" and "--[no-]progress" options.
+
+ * Various commands given by the end user (e.g. diff.type.textconv,
+   and GIT_EDITOR) can be specified with command line arguments.  E.g. it
+   is now possible to say "[diff "utf8doc"] textconv = nkf -w".
+
+ * "sparse checkout" feature allows only part of the work tree to be
+   checked out.
+
+ * HTTP transfer can use authentication scheme other than basic
+   (i.e./e.g. digest).
+
+ * Switching from a version of superproject that used to have a submodule
+   to another version of superproject that no longer has it did not remove
+   the submodule directory when it should (namely, when you are not
+   interested in the submodule at all and didn't clone/checkout).
+
+ * "git checkout A...B" is a way to detach HEAD at the merge base between
+   A and B.
+
+ * "git commit --date='<date>'" can be used to override the author date
+   just like "git commit --author='<name> <email>'" can be used to
+   override the author identity.
+
+ * "git commit --no-status" can be used to omit the listing of the index
+   and the work tree status in the editor used to prepare the log message.
+
+ * "git fetch --all" can now be used in place of "git remote update".
+
+ * "git push" learned "git push origin --delete branch", a syntactic sugar
+   for "git push origin :branch".
+
+ * "git rebase --onto A...B" means the history is replayed on top of the
+   merge base between A and B.
+
+ * Use of "git reset --merge" has become easier when resetting away a
+   conflicted mess left in the work tree.
+
+ * "git rerere" had rerere.autoupdate configuration but there was no way
+   to countermand it from the command line; --no-rerere-autoupdate option
+   given to "merge", "revert", etc. fixes this.
+
+ * "git status" learned "-s(hort)" output format.
+
+(developers)
+
+ * The infrastructure to build foreign SCM interface has been updated.
+
 
 Fixes since v1.6.6
 ------------------
@@ -59,6 +117,6 @@ release, unless otherwise noted.
 
 --
 exec >/var/tmp/1
-O=v1.6.6-27-g648f407
+O=v1.6.6-263-ge33fd3c
 echo O=$(git describe master)
 git shortlog --no-merges $O..master ^maint
index 76fc84d8780762e083cd4ca584b9d783b8c0cd81..c686f8646b465860c8a096241797709366cc4dc1 100644 (file)
@@ -279,6 +279,20 @@ from the list and queue it to 'pu', in order to make it easier for
 people play with it without having to pick up and apply the patch to
 their trees themselves.
 
+------------------------------------------------
+Know the status of your patch after submission
+
+* You can use Git itself to find out when your patch is merged in
+  master. 'git pull --rebase' will automatically skip already-applied
+  patches, and will let you know. This works only if you rebase on top
+  of the branch in which your patch has been merged (i.e. it will not
+  tell you if your patch is merged in pu if you rebase on top of
+  master).
+
+* Read the git mailing list, the maintainer regularly posts messages
+  entitled "What's cooking in git.git" and "What's in git.git" giving
+  the status of various proposed changes.
+
 ------------------------------------------------
 MUA specific hints
 
index 1625ffce6a1910c879447705fea4e3301039debd..4833cac4b996e83e351b70d8f02a160d04e9a8e3 100644 (file)
@@ -98,8 +98,10 @@ commit.
        files that were modified in the same commit.  This is
        useful when you reorganize your program and move code
        around across files.  When this option is given twice,
-       the command additionally looks for copies from all other
-       files in the parent for the commit that creates the file.
+       the command additionally looks for copies from other
+       files in the commit that creates the file. When this
+       option is given three times, the command additionally
+       looks for copies from other files in any commit.
 +
 <num> is optional but it is the lower bound on the number of
 alphanumeric characters that git must detect as moving
index 76173dbc5deaf1fdbff316b42e0a42ce61f4f32d..2d6775c135b466ce8a95db3fd7c22c04151f8d42 100644 (file)
@@ -297,17 +297,24 @@ false), while all other repositories are assumed to be bare (bare
 = true).
 
 core.worktree::
-       Set the path to the working tree.  The value will not be
-       used in combination with repositories found automatically in
-       a .git directory (i.e. $GIT_DIR is not set).
+       Set the path to the root of the work tree.
        This can be overridden by the GIT_WORK_TREE environment
        variable and the '--work-tree' command line option. It can be
-       a absolute path or relative path to the directory specified by
-       --git-dir or GIT_DIR.
-       Note: If --git-dir or GIT_DIR are specified but none of
+       an absolute path or a relative path to the .git directory,
+       either specified by --git-dir or GIT_DIR, or automatically
+       discovered.
+       If --git-dir or GIT_DIR are specified but none of
        --work-tree, GIT_WORK_TREE and core.worktree is specified,
-       the current working directory is regarded as the top directory
-       of your working tree.
+       the current working directory is regarded as the root of the
+       work tree.
++
+Note that this variable is honored even when set in a configuration
+file in a ".git" subdirectory of a directory, and its value differs
+from the latter directory (e.g. "/path/to/.git/config" has
+core.worktree set to "/different/path"), which is most likely a
+misconfiguration.  Running git commands in "/path/to" directory will
+still use "/different/path" as the root of the work tree and can cause
+great confusion to the users.
 
 core.logAllRefUpdates::
        Enable the reflog. Updates to a ref <ref> is logged to the file
@@ -495,6 +502,10 @@ notes should be printed.
 This setting defaults to "refs/notes/commits", and can be overridden by
 the `GIT_NOTES_REF` environment variable.
 
+core.sparseCheckout::
+       Enable "sparse checkout" feature. See section "Sparse checkout" in
+       linkgit:git-read-tree[1] for more information.
+
 add.ignore-errors::
        Tells 'git-add' to continue adding files when some files cannot be
        added due to indexing errors. Equivalent to the '--ignore-errors'
@@ -530,7 +541,7 @@ apply.whitespace::
        as the '--whitespace' option. See linkgit:git-apply[1].
 
 branch.autosetupmerge::
-       Tells 'git-branch' and 'git-checkout' to setup new branches
+       Tells 'git-branch' and 'git-checkout' to set up new branches
        so that linkgit:git-pull[1] will appropriately merge from the
        starting point branch. Note that even if this option is not set,
        this behavior can be chosen per-branch using the `--track`
@@ -705,6 +716,11 @@ color.ui::
        terminal. When more specific variables of color.* are set, they always
        take precedence over this setting. Defaults to false.
 
+commit.status
+       A boolean to enable/disable inclusion of status information in the
+       commit message template when using an editor to prepare the commit
+       message.  Defaults to true.
+
 commit.template::
        Specify a file to use as the template for new commit messages.
        "{tilde}/" is expanded to the value of `$HOME` and "{tilde}user/" to the
@@ -718,7 +734,7 @@ diff.autorefreshindex::
        contents in the work tree match the contents in the
        index.  This option defaults to true.  Note that this
        affects only 'git-diff' Porcelain, and not lower level
-       'diff' commands, such as 'git-diff-files'.
+       'diff' commands such as 'git-diff-files'.
 
 diff.external::
        If this config variable is set, diff generation is not
@@ -834,8 +850,8 @@ format.pretty::
 
 format.thread::
        The default threading style for 'git-format-patch'.  Can be
-       either a boolean value, `shallow` or `deep`.  `shallow`
-       threading makes every mail a reply to the head of the series,
+       a boolean value, or `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.
@@ -868,15 +884,12 @@ gc.autopacklimit::
        default value is 50.  Setting this to 0 disables it.
 
 gc.packrefs::
-       'git-gc' does not run `git pack-refs` in a bare repository by
-       default so that older dumb-transport clients can still fetch
-       from the repository.  Setting this to `true` lets 'git-gc'
-       to run `git pack-refs`.  Setting this to `false` tells
-       'git-gc' never to run `git pack-refs`. The default setting is
-       `notbare`. Enable it only when you know you do not have to
-       support such clients.  The default setting will change to `true`
-       at some stage, and setting this to `false` will continue to
-       prevent `git pack-refs` from being run from 'git-gc'.
+       Running `git pack-refs` in a repository renders it
+       unclonable by Git versions prior to 1.5.1.2 over dumb
+       transports such as HTTP.  This variable determines whether
+       'git gc' runs `git pack-refs`. This can be set to "nobare"
+       to enable it within all non-bare repos or it can be set to a
+       boolean value.  The default is `true`.
 
 gc.pruneexpire::
        When 'git-gc' is run, it will call 'prune --expire 2.weeks.ago'.
@@ -1132,6 +1145,12 @@ http.maxRequests::
        How many HTTP requests to launch in parallel. Can be overridden
        by the 'GIT_HTTP_MAX_REQUESTS' environment variable. Default is 5.
 
+http.minSessions::
+       The number of curl sessions (counted across slots) to be kept across
+       requests. They will not be ended with curl_easy_cleanup() until
+       http_cleanup() is invoked. If USE_CURL_MULTI is not defined, this
+       value will be capped at 1. Defaults to 1.
+
 http.postBuffer::
        Maximum size in bytes of the buffer used by smart HTTP
        transports when POSTing data to the remote system.
diff --git a/Documentation/date-formats.txt b/Documentation/date-formats.txt
new file mode 100644 (file)
index 0000000..c000f08
--- /dev/null
@@ -0,0 +1,26 @@
+DATE FORMATS
+------------
+
+The GIT_AUTHOR_DATE, GIT_COMMITTER_DATE environment variables
+ifdef::git-commit[]
+and the `--date` option
+endif::git-commit[]
+support the following date formats:
+
+Git internal format::
+       It is `<unix timestamp> <timezone offset>`, where `<unix
+       timestamp>` is the number of seconds since the UNIX epoch.
+       `<timezone offset>` is a positive or negative offset from UTC.
+       For example CET (which is 2 hours ahead UTC) is `+0200`.
+
+RFC 2822::
+       The standard email format as described by RFC 2822, for example
+       `Thu, 07 Apr 2005 22:13:13 +0200`.
+
+ISO 8601::
+       Time and date specified by the ISO 8601 standard, for example
+       `2005-04-07T22:13:13`. The parser accepts a space instead of the
+       `T` character as well.
++
+NOTE: In addition, the date part is accepted in the following formats:
+`YYYY.MM.DD`, `MM/DD/YYYY` and `DD.MM.YYYY`.
index e93e606f458356275964d0d7b438ca772abba47c..1f1b19996baa34b20bf08f4eb2889403cbc4c45d 100644 (file)
@@ -14,28 +14,32 @@ SYNOPSIS
 
 DESCRIPTION
 -----------
-This command adds the current content of new or modified files to the
-index, thus staging that content for inclusion in the next commit.
+This command updates the index using the current content found in
+the working tree, to prepare the content staged for the next commit.
+It typically adds the current content of existing paths as a whole,
+but with some options it can also be used to add content with
+only part of the changes made to the working tree files applied, or
+remove paths that do not exist in the working tree anymore.
 
 The "index" holds a snapshot of the content of the working tree, and it
 is this snapshot that is taken as the contents of the next commit.  Thus
 after making any changes to the working directory, and before running
-the commit command, you must use the 'add' command to add any new or
+the commit command, you must use the `add` command to add any new or
 modified files to the index.
 
 This command can be performed multiple times before a commit.  It only
 adds the content of the specified file(s) at the time the add command is
 run; if you want subsequent changes included in the next commit, then
-you must run 'git add' again to add the new content to the index.
+you must run `git add` again to add the new content to the index.
 
-The 'git status' command can be used to obtain a summary of which
+The `git status` command can be used to obtain a summary of which
 files have changes that are staged for the next commit.
 
-The 'git add' command will not add ignored files by default.  If any
-ignored files were explicitly specified on the command line, 'git add'
+The `git add` command will not add ignored files by default.  If any
+ignored files were explicitly specified on the command line, `git add`
 will fail with a list of ignored files.  Ignored files reached by
 directory recursion or filename globbing performed by Git (quote your
-globs before the shell) will be silently ignored.  The 'add' command can
+globs before the shell) will be silently ignored.  The `add` command can
 be used to add ignored files with the `-f` (force) option.
 
 Please see linkgit:git-commit[1] for alternative ways to add content to a
@@ -92,28 +96,31 @@ apply.
 
 -u::
 --update::
-       Update only files that git already knows about, staging modified
-       content for commit and marking deleted files for removal. This
-       is similar
-       to what "git commit -a" does in preparation for making a commit,
-       except that the update is limited to paths specified on the
-       command line. If no paths are specified, all tracked files in the
-       current directory and its subdirectories are updated.
+       Only match <filepattern> against already tracked files in
+       the index rather than the working tree. That means that it
+       will never stage new files, but that it will stage modified
+       new contents of tracked files and that it will remove files
+       from the index if the corresponding files in the working tree
+       have been removed.
++
+If no <filepattern> is given, default to "."; in other words,
+update all tracked files in the current directory and its
+subdirectories.
 
 -A::
 --all::
-       Update files that git already knows about (same as '\--update')
-       and add all untracked files that are not ignored by '.gitignore'
-       mechanism.
-
+       Like `-u`, but match <filepattern> against files in the
+       working tree in addition to the index. That means that it
+       will find new files as well as staging modified content and
+       removing files that are no longer in the working tree.
 
 -N::
 --intent-to-add::
        Record only the fact that the path will be added later. An entry
        for the path is placed in the index with no content. This is
        useful for, among other things, showing the unstaged content of
-       such files with 'git diff' and committing them with 'git commit
-       -a'.
+       such files with `git diff` and committing them with `git commit
+       -a`.
 
 --refresh::
        Don't add the file(s), but only refresh their stat()
@@ -133,7 +140,7 @@ apply.
 Configuration
 -------------
 
-The optional configuration variable 'core.excludesfile' indicates a path to a
+The optional configuration variable `core.excludesfile` indicates a path to a
 file containing patterns of file names to exclude from git-add, similar to
 $GIT_DIR/info/exclude.  Patterns in the exclude file are used in addition to
 those in info/exclude.  See linkgit:gitrepository-layout[5].
@@ -181,7 +188,7 @@ and type return, like this:
     What now> 1
 ------------
 
-You also could say "s" or "sta" or "status" above as long as the
+You also could say `s` or `sta` or `status` above as long as the
 choice is unique.
 
 The main command loop has 6 subcommands (plus help and quit).
@@ -189,9 +196,9 @@ The main command loop has 6 subcommands (plus help and quit).
 status::
 
    This shows the change between HEAD and index (i.e. what will be
-   committed if you say "git commit"), and between index and
+   committed if you say `git commit`), and between index and
    working tree files (i.e. what you could stage further before
-   "git commit" using "git-add") for each path.  A sample output
+   `git commit` using `git add`) for each path.  A sample output
    looks like this:
 +
 ------------
index 8c7b7b08386f69aaa96df2915566208d6abf3bf8..b786471dd8a9faa3a883969cc9745e53dee6831a 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git blame' [-c] [-b] [-l] [--root] [-t] [-f] [-n] [-s] [-p] [-w] [--incremental] [-L n,m]
-            [-S <revs-file>] [-M] [-C] [-C] [--since=<date>]
+           [-S <revs-file>] [-M] [-C] [-C] [-C] [--since=<date>]
            [<rev> | --contents <file> | --reverse <rev>] [--] <file>
 
 DESCRIPTION
index 7ccd742a87db1541184868b794fa4ef597d04de8..f43c8b2c08ab4ca45f759369dc3d8e85c9f76415 100644 (file)
@@ -96,13 +96,19 @@ objects from the source repository into a pack in the cloned repository.
 
 --quiet::
 -q::
-       Operate quietly.  This flag is also passed to the `rsync'
+       Operate quietly.  Progress is not reported to the standard
+       error stream. This flag is also passed to the `rsync'
        command when given.
 
 --verbose::
 -v::
-       Display the progress bar, even in case the standard output is not
-       a terminal.
+       Run verbosely.
+
+--progress::
+       Progress status is reported on the standard error stream
+       by default when it is attached to a terminal, unless -q
+       is specified. This flag forces progress status even if the
+       standard error stream is not directed to a terminal.
 
 --no-checkout::
 -n::
index b8834baced4b25d6df47eb578c4d745d23e8cc73..4fec5d5e3869d1f963c935534799c22fc52c46a8 100644 (file)
@@ -73,6 +73,7 @@ A commit comment is read from stdin. If a changelog
 entry is not provided via "<" redirection, 'git-commit-tree' will just wait
 for one to be entered and terminated with ^D.
 
+include::date-formats.txt[]
 
 Diagnostics
 -----------
index c97c151ae1fdf53a09c5d3796ea6f6fb98b57b03..d3a2dec21eb0323b535dfc696bceac3d6b66b2c7 100644 (file)
@@ -11,7 +11,8 @@ SYNOPSIS
 'git commit' [-a | --interactive] [-s] [-v] [-u<mode>] [--amend] [--dry-run]
           [(-c | -C) <commit>] [-F <file> | -m <msg>] [--reset-author]
           [--allow-empty] [--no-verify] [-e] [--author=<author>]
-          [--cleanup=<mode>] [--] [[-i | -o ]<file>...]
+          [--date=<date>] [--cleanup=<mode>] [--status | --no-status] [--]
+          [[-i | -o ]<file>...]
 
 DESCRIPTION
 -----------
@@ -99,6 +100,9 @@ OPTIONS
        an existing commit that matches the given string and its author
        name is used.
 
+--date=<date>::
+       Override the author date used in the commit.
+
 -m <msg>::
 --message=<msg>::
        Use the given <msg> as the commit message.
@@ -221,6 +225,17 @@ specified.
        to be committed, paths with local changes that will be left
        uncommitted and paths that are untracked.
 
+--status::
+       Include the output of linkgit:git-status[1] in the commit
+       message template when using an editor to prepare the commit
+       message.  Defaults to on, but can be used to override
+       configuration variable commit.status.
+
+--no-status::
+       Do not include the output of linkgit:git-status[1] in the
+       commit message template when using an editor to prepare the
+       default commit message.
+
 \--::
        Do not interpret any more arguments as options.
 
@@ -231,6 +246,8 @@ specified.
        these files are also staged for the next commit on top
        of what have been staged before.
 
+:git-commit: 1
+include::date-formats.txt[]
 
 EXAMPLES
 --------
index f68b198205d3a4d808ce58e6b0ac03e70bb160a4..263292809d58b18becbbb73fafb10681436a76c3 100644 (file)
@@ -37,11 +37,12 @@ existing values that match the regexp are updated or unset.  If
 you want to handle the lines that do *not* match the regex, just
 prepend a single exclamation mark in front (see also <<EXAMPLES>>).
 
-The type specifier can be either '--int' or '--bool', which will make
+The type specifier can be either '--int' or '--bool', to make
 'git-config' ensure that the variable(s) are of the given type and
 convert the value to the canonical form (simple decimal number for int,
-a "true" or "false" string for bool).  If no type specifier is passed,
-no checks or transformations are performed on the value.
+a "true" or "false" string for bool), or '--path', which does some
+path expansion (see '--path' below).  If no type specifier is passed, no
+checks or transformations are performed on the value.
 
 The file-option can be one of '--system', '--global' or '--file'
 which specify where the values will be read from or written to.
@@ -136,6 +137,13 @@ See also <<FILES>>.
        'git-config' will ensure that the output matches the format of
        either --bool or --int, as described above.
 
+--path::
+       'git-config' will expand leading '{tilde}' to the value of
+       '$HOME', and '{tilde}user' to the home directory for the
+       specified user.  This option has no effect when setting the
+       value (but you can use 'git config bla {tilde}/' from the
+       command line to let your shell do the expansion).
+
 -z::
 --null::
        For all options that output values and/or keys, always
index 99a7c14700ffa06090534682283320fef5001815..fbab29550a49ea6a187338a6828b9db61f158a15 100644 (file)
@@ -277,6 +277,21 @@ In `dbdriver` and `dbuser` you can use the following variables:
        If no name can be determined, the
        numeric uid is used.
 
+ENVIRONMENT
+-----------
+
+These variables obviate the need for command-line options in some
+circumstances, allowing easier restricted usage through git-shell.
+
+GIT_CVSSERVER_BASE_PATH takes the place of the argument to --base-path.
+
+GIT_CVSSERVER_ROOT specifies a single-directory whitelist. The
+repository must still be configured to allow access through
+git-cvsserver, as described above.
+
+When these environment variables are set, the corresponding
+command-line arguments may not be used.
+
 Eclipse CVS Client Notes
 ------------------------
 
index 288032c7b88f2f43a2965d5bdbffc8472f0ca281..ae87f0922781453f8a06c72f332dcacd4af1e968 100644 (file)
@@ -75,6 +75,20 @@ OPTIONS
        set of marks.  If a mark is defined to different values,
        the last file wins.
 
+--relative-marks::
+       After specifying --relative-marks= the paths specified
+       with --import-marks= and --export-marks= are relative
+       to an internal directory in the current repository.
+       In git-fast-import this means that the paths are relative
+       to the .git/info/fast-import directory. However, other
+       importers may use a different location.
+
+--no-relative-marks::
+       Negates a previous --relative-marks. Allows for combining
+       relative and non-relative marks by interweaving
+       --(no-)-relative-marks= with the --(import|export)-marks=
+       options.
+
 --export-pack-edges=<file>::
        After creating a packfile, print a line of data to
        <file> listing the filename of the packfile and the last
@@ -303,6 +317,15 @@ and control the current import process.  More detailed discussion
        standard output.  This command is optional and is not needed
        to perform an import.
 
+`feature`::
+       Require that fast-import supports the specified feature, or
+       abort if it does not.
+
+`option`::
+       Specify any of the options listed under OPTIONS that do not
+       change stream semantic to suit the frontend's needs. This
+       command is optional and is not needed to perform an import.
+
 `commit`
 ~~~~~~~~
 Create or update a branch with a new commit, recording one logical
@@ -311,8 +334,8 @@ change to the project.
 ....
        'commit' SP <ref> LF
        mark?
-       ('author' SP <name> SP LT <email> GT SP <when> LF)?
-       'committer' SP <name> SP LT <email> GT SP <when> LF
+       ('author' (SP <name>)? SP LT <email> GT SP <when> LF)?
+       'committer' (SP <name>)? SP LT <email> GT SP <when> LF
        data
        ('from' SP <committish> LF)?
        ('merge' SP <committish> LF)?
@@ -657,7 +680,7 @@ lightweight (non-annotated) tags see the `reset` command below.
 ....
        'tag' SP <name> LF
        'from' SP <committish> LF
-       'tagger' SP <name> SP LT <email> GT SP <when> LF
+       'tagger' (SP <name>)? SP LT <email> GT SP <when> LF
        data
 ....
 
@@ -846,6 +869,62 @@ Placing a `progress` command immediately after a `checkpoint` will
 inform the reader when the `checkpoint` has been completed and it
 can safely access the refs that fast-import updated.
 
+`feature`
+~~~~~~~~~
+Require that fast-import supports the specified feature, or abort if
+it does not.
+
+....
+       'feature' SP <feature> LF
+....
+
+The <feature> part of the command may be any string matching
+^[a-zA-Z][a-zA-Z-]*$ and should be understood by fast-import.
+
+Feature work identical as their option counterparts with the
+exception of the import-marks feature, see below.
+
+The following features are currently supported:
+
+* date-format
+* import-marks
+* export-marks
+* relative-marks
+* no-relative-marks
+* force
+
+The import-marks behaves differently from when it is specified as
+commandline option in that only one "feature import-marks" is allowed
+per stream. Also, any --import-marks= specified on the commandline
+will override those from the stream (if any).
+
+`option`
+~~~~~~~~
+Processes the specified option so that git fast-import behaves in a
+way that suits the frontend's needs.
+Note that options specified by the frontend are overridden by any
+options the user may specify to git fast-import itself.
+
+....
+    'option' SP <option> LF
+....
+
+The `<option>` part of the command may contain any of the options
+listed in the OPTIONS section that do not change import semantics,
+without the leading '--' and is treated in the same way.
+
+Option commands must be the first commands on the input (not counting
+feature commands), to give an option command after any non-option
+command is an error.
+
+The following commandline options change import semantics and may therefore
+not be passed as option:
+
+* date-format
+* import-marks
+* export-marks
+* force
+
 Crash Reports
 -------------
 If fast-import is supplied invalid input it will terminate with a
index 67aec067c8ffd75837ccc1d3e2524f5db2eb9a75..c8fe08a0c4e2c5c561ce1d653f3a722b511eca28 100644 (file)
@@ -18,6 +18,11 @@ The program supports clients fetching using both the smart HTTP protcol
 and the backwards-compatible dumb HTTP protocol, as well as clients
 pushing using the smart HTTP protocol.
 
+It verifies that the directory has the magic file
+"git-daemon-export-ok", and it will refuse to export any git directory
+that hasn't explicitly been marked for export this way (unless the
+GIT_HTTP_EXPORT_ALL environmental variable is set).
+
 By default, only the `upload-pack` service is enabled, which serves
 'git-fetch-pack' and 'git-ls-remote' clients, which are invoked from
 'git-fetch', 'git-pull', and 'git-clone'.  If the client is authenticated,
@@ -70,6 +75,7 @@ Apache 2.x::
 +
 ----------------------------------------------------------------
 SetEnv GIT_PROJECT_ROOT /var/www/git
+SetEnv GIT_HTTP_EXPORT_ALL
 ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
 ----------------------------------------------------------------
 +
@@ -157,6 +163,10 @@ by the invoking web server, including:
 * QUERY_STRING
 * REQUEST_METHOD
 
+The GIT_HTTP_EXPORT_ALL environmental variable may be passed to
+'git-http-backend' to bypass the check for the "git-daemon-export-ok"
+file in each repository before allowing export of that repository.
+
 The backend process sets GIT_COMMITTER_NAME to '$REMOTE_USER' and
 GIT_COMMITTER_EMAIL to '$\{REMOTE_USER}@http.$\{REMOTE_ADDR\}',
 ensuring that any reflogs created by 'git-receive-pack' contain some
index 625723e41fa072523fa850e37534677cfe8af631..98f3b9e7589cd810d0bc04f08e84dd057039e258 100644 (file)
@@ -109,6 +109,7 @@ OPTIONS
        Identify the file status with the following tags (followed by
        a space) at the start of each line:
        H::     cached
+       S::     skip-worktree
        M::     unmerged
        R::     removed/deleted
        C::     modified/changed
index e886c2ef543501deea84909b2e88fb163a1c9d2b..67470311e24a4ae6837adeaaec84ed24a6a4ae1f 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git merge' [-n] [--stat] [--no-commit] [--squash] [-s <strategy>]...
-       [-m <msg>] <remote>...
+        [--[no-]rerere-autoupdate] [-m <msg>] <remote>...
 'git merge' <msg> HEAD <remote>...
 
 DESCRIPTION
@@ -33,6 +33,11 @@ include::merge-options.txt[]
        used to give a good default for automated 'git merge'
        invocations.
 
+--rerere-autoupdate::
+--no-rerere-autoupdate::
+       Allow the rerere mechanism to update the index with the
+       result of auto-conflict resolution if possible.
+
 <remote>...::
        Other branch heads to merge into our branch.  You need at
        least one <remote>.  Specifying more than one <remote>
index 52c0538df528ecbb5b755a2e75fc1715762bfc00..e3eb1e8f193f4f60fb2190d8335ba347f7fccb32 100644 (file)
@@ -91,6 +91,10 @@ nor in any Push line of the corresponding remotes file---see below).
        will be tab-separated and sent to stdout instead of stderr.  The full
        symbolic names of the refs will be given.
 
+--delete::
+       All listed refs are deleted from the remote repository. This is
+       the same as prefixing all refs with a colon.
+
 --tags::
        All refs under `$GIT_DIR/refs/tags` are pushed, in
        addition to refspecs explicitly listed on the command
index a10ce4ba40cc509a0f3a8f39551310fdd9231e71..d6faa141495521b2f7281040364d540a7cf41bb3 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 'git read-tree' [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>]
                [-u [--exclude-per-directory=<gitignore>] | -i]]
-               [--index-output=<file>]
+               [--index-output=<file>] [--no-sparse-checkout]
                <tree-ish1> [<tree-ish2> [<tree-ish3>]]
 
 
@@ -110,6 +110,10 @@ OPTIONS
        directories the index file and index output file are
        located in.
 
+--no-sparse-checkout::
+       Disable sparse checkout support even if `core.sparseCheckout`
+       is true.
+
 <tree-ish#>::
        The id of the tree object(s) to be read/merged.
 
@@ -360,6 +364,52 @@ middle of doing, and when your working tree is ready (i.e. you
 have finished your work-in-progress), attempt the merge again.
 
 
+Sparse checkout
+---------------
+
+"Sparse checkout" allows to sparsely populate working directory.
+It uses skip-worktree bit (see linkgit:git-update-index[1]) to tell
+Git whether a file on working directory is worth looking at.
+
+"git read-tree" and other merge-based commands ("git merge", "git
+checkout"...) can help maintaining skip-worktree bitmap and working
+directory update. `$GIT_DIR/info/sparse-checkout` is used to
+define the skip-worktree reference bitmap. When "git read-tree" needs
+to update working directory, it will reset skip-worktree bit in index
+based on this file, which uses the same syntax as .gitignore files.
+If an entry matches a pattern in this file, skip-worktree will be
+set on that entry. Otherwise, skip-worktree will be unset.
+
+Then it compares the new skip-worktree value with the previous one. If
+skip-worktree turns from unset to set, it will add the corresponding
+file back. If it turns from set to unset, that file will be removed.
+
+While `$GIT_DIR/info/sparse-checkout` is usually used to specify what
+files are in. You can also specify what files are _not_ in, using
+negate patterns. For example, to remove file "unwanted":
+
+----------------
+*
+!unwanted
+----------------
+
+Another tricky thing is fully repopulating working directory when you
+no longer want sparse checkout. You cannot just disable "sparse
+checkout" because skip-worktree are still in the index and you working
+directory is still sparsely populated. You should re-populate working
+directory with the `$GIT_DIR/info/sparse-checkout` file content as
+follows:
+
+----------------
+*
+----------------
+
+Then you can disable sparse checkout. Sparse checkout support in "git
+read-tree" and similar commands is disabled by default. You need to
+turn `core.sparseCheckout` on in order to have sparse checkout
+support.
+
+
 SEE ALSO
 --------
 linkgit:git-write-tree[1]; linkgit:git-ls-files[1];
index 5cfdc0cfc553dc01f6f1c7cb185e79449ee0b0a8..4685a898f153091d2b747bfb204cf0686c6a8f82 100644 (file)
@@ -25,7 +25,10 @@ Commands are given by the caller on the helper's standard input, one per line.
 
 'capabilities'::
        Lists the capabilities of the helper, one per line, ending
-       with a blank line.
+       with a blank line. Each capability may be preceeded with '*'.
+       This marks them mandatory for git version using the remote
+       helper to understand (unknown mandatory capability is fatal
+       error).
 
 'list'::
        Lists the refs, one per line, in the format "<value> <name>
@@ -90,6 +93,20 @@ Supported if the helper has the "push" capability.
 +
 Supported if the helper has the "import" capability.
 
+'connect' <service>::
+       Connects to given service. Standard input and standard output
+       of helper are connected to specified service (git prefix is
+       included in service name so e.g. fetching uses 'git-upload-pack'
+       as service) on remote side. Valid replies to this command are
+       empty line (connection established), 'fallback' (no smart
+       transport support, fall back to dumb transports) and just
+       exiting with error message printed (can't connect, don't
+       bother trying to fall back). After line feed terminating the
+       positive (empty) response, the output of service starts. After
+       the connection ends, the remote helper exits.
++
+Supported if the helper has the "connect" capability.
+
 If a fatal error occurs, the program writes the error message to
 stderr and exits. The caller should expect that a suitable error
 message has been printed if the child closes the connection without
@@ -123,6 +140,9 @@ CAPABILITIES
        all, it must cover all refs reported by the list command; if
        it is not used, it is effectively "*:*"
 
+'connect'::
+       This helper supports the 'connect' command.
+
 REF LIST ATTRIBUTES
 -------------------
 
@@ -165,9 +185,15 @@ OPTIONS
        but don't actually change any repository data.  For most
        helpers this only applies to the 'push', if supported.
 
+'option servpath <c-style-quoted-path>'::
+       Set service path (--upload-pack, --receive-pack etc.) for
+       next connect. Remote helper MAY support this option. Remote
+       helper MUST NOT rely on this option being set before
+       connect request occurs.
+
 Documentation
 -------------
-Documentation by Daniel Barkalow.
+Documentation by Daniel Barkalow and Ilari Liusvaara
 
 GIT
 ---
index 2d27e405a39c77f1a7d7507db2f063a4819bec47..c7aa444317855b5b1c00c15284a16fdd4c4cb3ad 100644 (file)
@@ -62,11 +62,101 @@ This means that `git reset -p` is the opposite of `git add -p` (see
 linkgit:git-add[1]).
 
 -q::
+--quiet::
        Be quiet, only report errors.
 
 <commit>::
        Commit to make the current HEAD. If not given defaults to HEAD.
 
+DISCUSSION
+----------
+
+The tables below show what happens when running:
+
+----------
+git reset --option target
+----------
+
+to reset the HEAD to another commit (`target`) with the different
+reset options depending on the state of the files.
+
+In these tables, A, B, C and D are some different states of a
+file. For example, the first line of the first table means that if a
+file is in state A in the working tree, in state B in the index, in
+state C in HEAD and in state D in the target, then "git reset --soft
+target" will put the file in state A in the working tree, in state B
+in the index and in state D in HEAD.
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       A       B     C    D     --soft   A       B     D
+                               --mixed  A       D     D
+                               --hard   D       D     D
+                               --merge (disallowed)
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       A       B     C    C     --soft   A       B     C
+                               --mixed  A       C     C
+                               --hard   C       C     C
+                               --merge (disallowed)
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       B       B     C    D     --soft   B       B     D
+                               --mixed  B       D     D
+                               --hard   D       D     D
+                               --merge  D       D     D
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       B       B     C    C     --soft   B       B     C
+                               --mixed  B       C     C
+                               --hard   C       C     C
+                               --merge  C       C     C
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       B       C     C    D     --soft   B       C     D
+                               --mixed  B       D     D
+                               --hard   D       D     D
+                               --merge (disallowed)
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       B       C     C    C     --soft   B       C     C
+                               --mixed  B       C     C
+                               --hard   C       C     C
+                               --merge  B       C     C
+
+"reset --merge" is meant to be used when resetting out of a conflicted
+merge. Any mergy operation guarantees that the work tree file that is
+involved in the merge does not have local change wrt the index before
+it starts, and that it writes the result out to the work tree. So if
+we see some difference between the index and the target and also
+between the index and the work tree, then it means that we are not
+resetting out from a state that a mergy operation left after failing
+with a conflict. That is why we disallow --merge option in this case.
+
+The following tables show what happens when there are unmerged
+entries:
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       X       U     A    B     --soft  (disallowed)
+                               --mixed  X       B     B
+                               --hard   B       B     B
+                               --merge  B       B     B
+
+      working index HEAD target         working index HEAD
+      ----------------------------------------------------
+       X       U     A    A     --soft  (disallowed)
+                               --mixed  X       A     A
+                               --hard   A       A     A
+                               --merge  A       A     A
+
+X means any state and U means an unmerged index.
+
 Examples
 --------
 
index 82045a2522799ccf3a03479bf4f4cd1fa1809879..dc829b333d9bda935c01581655fe62910b8f6e8f 100644 (file)
@@ -112,6 +112,9 @@ OPTIONS
 --remotes::
        Show tag refs found in `$GIT_DIR/refs/remotes`.
 
+--show-toplevel::
+       Show the absolute path of the top-level directory.
+
 --show-prefix::
        When the command is invoked from a subdirectory, show the
        path of the current directory relative to the top-level
index 5afb1e7428126c79171cf7e7b1fb027e1de64c86..c21d19e573d5192597b4766d8d87d17ab8f1c0f3 100644 (file)
@@ -12,13 +12,13 @@ SYNOPSIS
 DESCRIPTION
 -----------
 Remove files from the index, or from the working tree and the index.
-'git-rm' will not remove a file from just your working directory.
-(There is no option to remove a file only from the work tree
+`git rm` will not remove a file from just your working directory.
+(There is no option to remove a file only from the working tree
 and yet keep it in the index; use `/bin/rm` if you want to do that.)
 The files being removed have to be identical to the tip of the branch,
 and no updates to their contents can be staged in the index,
 though that default behavior can be overridden with the `-f` option.
-When '--cached' is given, the staged content has to
+When `--cached` is given, the staged content has to
 match either the tip of the branch or the file on disk,
 allowing the file to be removed from just the index.
 
@@ -64,7 +64,7 @@ OPTIONS
 
 -q::
 --quiet::
-       'git-rm' normally outputs one line (in the form of an "rm" command)
+       `git rm` normally outputs one line (in the form of an `rm` command)
        for each file removed. This option suppresses that output.
 
 
@@ -81,6 +81,58 @@ two directories `d` and `d2`, there is a difference between
 using `git rm \'d\*\'` and `git rm \'d/\*\'`, as the former will
 also remove all of directory `d2`.
 
+REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM
+--------------------------------------------------------
+There is no option for `git rm` to remove from the index only
+the paths that have disappeared from the filesystem. However,
+depending on the use case, there are several ways that can be
+done.
+
+Using "git commit -a"
+~~~~~~~~~~~~~~~~~~~~~
+If you intend that your next commit should record all modifications
+of tracked files in the working tree and record all removals of
+files that have been removed from the working tree with `rm`
+(as opposed to `git rm`), use `git commit -a`, as it will
+automatically notice and record all removals.  You can also have a
+similar effect without committing by using `git add -u`.
+
+Using "git add -A"
+~~~~~~~~~~~~~~~~~~
+When accepting a new code drop for a vendor branch, you probably
+want to record both the removal of paths and additions of new paths
+as well as modifications of existing paths.
+
+Typically you would first remove all tracked files from the working
+tree using this command:
+
+----------------
+git ls-files -z | xargs -0 rm -f
+----------------
+
+and then "untar" the new code in the working tree. Alternately
+you could "rsync" the changes into the working tree.
+
+After that, the easiest way to record all removals, additions, and
+modifications in the working tree is:
+
+----------------
+git add -A
+----------------
+
+See linkgit:git-add[1].
+
+Other ways
+~~~~~~~~~~
+If all you really want to do is to remove from the index the files
+that are no longer present in the working tree (perhaps because
+your working tree is dirty so that you cannot use `git commit -a`),
+use the following command:
+
+----------------
+git diff --name-only --diff-filter=D -z | xargs -0 git rm --cached
+----------------
+
 EXAMPLES
 --------
 git rm Documentation/\\*.txt::
index 6052484ab9e28d5565067885b1c4ed5667e8e90f..8d88018eedd08511e2676013d88c8d44b10e97ac 100644 (file)
@@ -15,6 +15,7 @@ SYNOPSIS
             [--cacheinfo <mode> <object> <file>]\*
             [--chmod=(+|-)x]
             [--assume-unchanged | --no-assume-unchanged]
+            [--skip-worktree | --no-skip-worktree]
             [--ignore-submodules]
             [--really-refresh] [--unresolve] [--again | -g]
             [--info-only] [--index-info]
@@ -103,6 +104,13 @@ you will need to handle the situation manually.
        Like '--refresh', but checks stat information unconditionally,
        without regard to the "assume unchanged" setting.
 
+--skip-worktree::
+--no-skip-worktree::
+       When one of these flags is specified, the object name recorded
+       for the paths are not updated. Instead, these options
+       set and unset the "skip-worktree" bit for the paths. See
+       section "Skip-worktree bit" below for more information.
+
 -g::
 --again::
        Runs 'git-update-index' itself on the paths whose index
@@ -308,6 +316,27 @@ M foo.c
 <9> now it checks with lstat(2) and finds it has been changed.
 
 
+Skip-worktree bit
+-----------------
+
+Skip-worktree bit can be defined in one (long) sentence: When reading
+an entry, if it is marked as skip-worktree, then Git pretends its
+working directory version is up to date and read the index version
+instead.
+
+To elaborate, "reading" means checking for file existence, reading
+file attributes or file content. The working directory version may be
+present or absent. If present, its content may match against the index
+version or not. Writing is not affected by this bit, content safety
+is still first priority. Note that Git _can_ update working directory
+file, that is marked skip-worktree, if it is safe to do so (i.e.
+working directory version matches index version)
+
+Although this bit looks similar to assume-unchanged bit, its goal is
+different from assume-unchanged bit's. Skip-worktree also takes
+precedence over assume-unchanged bit when both are set.
+
+
 Configuration
 -------------
 
index 352c23019f7dad59c735f88c0606cf056c84a578..b6df39ba36eddd640c456293a0233894beb1da5c 100644 (file)
@@ -43,14 +43,16 @@ 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.6/git.html[documentation for release 1.6.6]
+* link:v1.6.6.1/git.html[documentation for release 1.6.6.1]
 
 * release notes for
+  link:RelNotes-1.6.6.1.txt[1.6.6.1],
   link:RelNotes-1.6.6.txt[1.6.6].
 
-* link:v1.6.5.7/git.html[documentation for release 1.6.5.7]
+* link:v1.6.5.8/git.html[documentation for release 1.6.5.8]
 
 * release notes for
+  link:RelNotes-1.6.5.8.txt[1.6.5.8],
   link:RelNotes-1.6.5.7.txt[1.6.5.7],
   link:RelNotes-1.6.5.6.txt[1.6.5.6],
   link:RelNotes-1.6.5.5.txt[1.6.5.5],
index 5bbd18f0206604416c3833b7541a5b55b7e63976..add6f435b59e3df095e507d8a5d4f911cee9d3b2 100644 (file)
@@ -58,6 +58,9 @@ The result of the enumeration is left in these fields::
 Calling sequence
 ----------------
 
+Note: index may be looked at for .gitignore files that are CE_SKIP_WORKTREE
+marked. If you to exclude files, make sure you have loaded index first.
+
 * Prepare `struct dir_struct dir` and clear it with `memset(&dir, 0,
   sizeof(dir))`.
 
diff --git a/INSTALL b/INSTALL
index be504c95e1f092edaf524064d49ba30adc57dc9b..61086ab1204a4304cb1d84eeea9d1649878ac9e1 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -38,13 +38,17 @@ Issues of note:
    Interactive Tools package still can install "git", but you can build it
    with --disable-transition option to avoid this.
 
- - You can use git after building but without installing if you
-   wanted to.  Various git commands need to find other git
-   commands and scripts to do their work, so you would need to
-   arrange a few environment variables to tell them that their
-   friends will be found in your built source area instead of at
-   their standard installation area.  Something like this works
-   for me:
+ - You can use git after building but without installing if you want
+   to test drive it.  Simply run git found in bin-wrappers directory
+   in the build directory, or prepend that directory to your $PATH.
+   This however is less efficient than running an installed git, as
+   you always need an extra fork+exec to run any git subcommand.
+
+   It is still possible to use git without installing by setting a few
+   environment variables, which was the way this was done
+   traditionally.  But using git found in bin-wrappers directory in
+   the build directory is far simpler.  As a historical reference, the
+   old way went like this:
 
        GIT_EXEC_PATH=`pwd`
        PATH=`pwd`:$PATH
index c11719c277de97d48c21f1207a2e9bc24f07ebad..b7f56b7eaa8b29cd601c2859748531df3f651fb3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -222,7 +222,7 @@ all::
 #   DEFAULT_EDITOR='$GIT_FALLBACK_EDITOR',
 #   DEFAULT_EDITOR='"C:\Program Files\Vim\gvim.exe" --nofork'
 
-GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
+GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
 -include GIT-VERSION-FILE
 
@@ -431,6 +431,15 @@ ALL_PROGRAMS = $(PROGRAMS) $(SCRIPTS)
 # what 'all' will build but not install in gitexecdir
 OTHER_PROGRAMS = git$X
 
+# what test wrappers are needed and 'install' will install, in bindir
+BINDIR_PROGRAMS_NEED_X += git
+BINDIR_PROGRAMS_NEED_X += git-upload-pack
+BINDIR_PROGRAMS_NEED_X += git-receive-pack
+BINDIR_PROGRAMS_NEED_X += git-upload-archive
+BINDIR_PROGRAMS_NEED_X += git-shell
+
+BINDIR_PROGRAMS_NO_X += git-cvsserver
+
 # Set paths to tools early so that they can be used for version tests.
 ifndef SHELL_PATH
        SHELL_PATH = /bin/sh
@@ -459,6 +468,7 @@ LIB_H += commit.h
 LIB_H += compat/bswap.h
 LIB_H += compat/cygwin.h
 LIB_H += compat/mingw.h
+LIB_H += compat/win32/pthread.h
 LIB_H += csum-file.h
 LIB_H += decorate.h
 LIB_H += delta.h
@@ -976,15 +986,16 @@ ifeq ($(uname_S),Windows)
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
        NO_CURL = YesPlease
-       NO_PTHREADS = YesPlease
+       NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
+       THREADED_DELTA_SEARCH = YesPlease
 
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
        CFLAGS =
        BASIC_CFLAGS = -nologo -I. -I../zlib -Icompat/vcbuild -Icompat/vcbuild/include -DWIN32 -D_CONSOLE -DHAVE_STRING_H -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE
-       COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o
-       COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -DSTRIP_EXTENSION=\".exe\"
+       COMPAT_OBJS = compat/msvc.o compat/fnmatch/fnmatch.o compat/winansi.o compat/win32/pthread.o
+       COMPAT_CFLAGS = -D__USE_MINGW_ACCESS -DNOGDI -DHAVE_STRING_H -DHAVE_ALLOCA_H -Icompat -Icompat/fnmatch -Icompat/regex -Icompat/fnmatch -Icompat/win32 -DSTRIP_EXTENSION=\".exe\"
        BASIC_LDFLAGS = -IGNORE:4217 -IGNORE:4049 -NOLOGO -SUBSYSTEM:CONSOLE -NODEFAULTLIB:MSVCRT.lib
        EXTLIBS = advapi32.lib shell32.lib wininet.lib ws2_32.lib
        lib =
@@ -1026,10 +1037,13 @@ ifneq (,$(findstring MINGW,$(uname_S)))
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
+       NO_PYTHON = YesPlease
        BLK_SHA1 = YesPlease
+       THREADED_DELTA_SEARCH = 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/winansi.o
+       COMPAT_OBJS += compat/mingw.o compat/fnmatch/fnmatch.o compat/winansi.o \
+               compat/win32/pthread.o
        EXTLIBS += -lws2_32
        X = .exe
 ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
@@ -1039,10 +1053,8 @@ ifneq (,$(wildcard ../THIS_IS_MSYSGIT))
        EXTLIBS += /mingw/lib/libz.a
        NO_R_TO_GCC_LINKER = YesPlease
        INTERNAL_QSORT = YesPlease
-       THREADED_DELTA_SEARCH = YesPlease
 else
        NO_CURL = YesPlease
-       NO_PTHREADS = YesPlease
 endif
 endif
 
@@ -1090,6 +1102,9 @@ endif
 
 ifdef NO_CURL
        BASIC_CFLAGS += -DNO_CURL
+       REMOTE_CURL_PRIMARY =
+       REMOTE_CURL_ALIASES =
+       REMOTE_CURL_NAMES =
 else
        ifdef CURLDIR
                # Try "-Wl,-rpath=$(CURLDIR)/$(lib)" in such a case.
@@ -1098,7 +1113,10 @@ else
        else
                CURL_LIBCURL = -lcurl
        endif
-       PROGRAMS += git-remote-curl$X git-http-fetch$X
+       REMOTE_CURL_PRIMARY = git-remote-http$X
+       REMOTE_CURL_ALIASES = git-remote-https$X git-remote-ftp$X git-remote-ftps$X
+       REMOTE_CURL_NAMES = $(REMOTE_CURL_PRIMARY) $(REMOTE_CURL_ALIASES)
+       PROGRAMS += $(REMOTE_CURL_NAMES) git-http-fetch$X
        curl_check := $(shell (echo 070908; curl-config --vernum) | sort -r | sed -ne 2p)
        ifeq "$(curl_check)" "070908"
                ifndef NO_EXPAT
@@ -1467,20 +1485,19 @@ shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell
 strip: $(PROGRAMS) git$X
        $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
 
-git.o: git.c common-cmds.h GIT-CFLAGS
-       $(QUIET_CC)$(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
-               '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
-               $(ALL_CFLAGS) -o $@ -c $(filter %.c,$^)
+git.o: common-cmds.h
+git.s git.o: ALL_CFLAGS += -DGIT_VERSION='"$(GIT_VERSION)"' \
+       '-DGIT_HTML_PATH="$(htmldir_SQ)"'
 
 git$X: git.o $(BUILTIN_OBJS) $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ git.o \
                $(BUILTIN_OBJS) $(ALL_LDFLAGS) $(LIBS)
 
-builtin-help.o: builtin-help.c common-cmds.h GIT-CFLAGS
-       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
-               '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
-               '-DGIT_MAN_PATH="$(mandir_SQ)"' \
-               '-DGIT_INFO_PATH="$(infodir_SQ)"' $<
+builtin-help.o: common-cmds.h
+builtin-help.s builtin-help.o: ALL_CFLAGS += \
+       '-DGIT_HTML_PATH="$(htmldir_SQ)"' \
+       '-DGIT_MAN_PATH="$(mandir_SQ)"' \
+       '-DGIT_INFO_PATH="$(infodir_SQ)"'
 
 $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && \
@@ -1633,30 +1650,26 @@ git.o git.spec \
 
 %.o: %.c GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
-%.s: %.c GIT-CFLAGS
+%.s: %.c GIT-CFLAGS FORCE
        $(QUIET_CC)$(CC) -S $(ALL_CFLAGS) $<
-%.o: %.S
+%.o: %.S GIT-CFLAGS
        $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) $<
 
-exec_cmd.o: exec_cmd.c GIT-CFLAGS
-       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) \
-               '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
-               '-DBINDIR="$(bindir_relative_SQ)"' \
-               '-DPREFIX="$(prefix_SQ)"' \
-               $<
+exec_cmd.s exec_cmd.o: ALL_CFLAGS += \
+       '-DGIT_EXEC_PATH="$(gitexecdir_SQ)"' \
+       '-DBINDIR="$(bindir_relative_SQ)"' \
+       '-DPREFIX="$(prefix_SQ)"'
 
-builtin-init-db.o: builtin-init-db.c GIT-CFLAGS
-       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"' $<
+builtin-init-db.s builtin-init-db.o: ALL_CFLAGS += \
+       -DDEFAULT_GIT_TEMPLATE_DIR='"$(template_dir_SQ)"'
 
-config.o: config.c GIT-CFLAGS
-       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"' $<
+config.s config.o: ALL_CFLAGS += -DETC_GITCONFIG='"$(ETC_GITCONFIG_SQ)"'
 
-http.o: http.c GIT-CFLAGS
-       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DGIT_USER_AGENT='"git/$(GIT_VERSION)"' $<
+http.s http.o: ALL_CFLAGS += -DGIT_USER_AGENT='"git/$(GIT_VERSION)"'
 
 ifdef NO_EXPAT
-http-walker.o: http-walker.c http.h GIT-CFLAGS
-       $(QUIET_CC)$(CC) -o $*.o -c $(ALL_CFLAGS) -DNO_EXPAT $<
+http-walker.o: http.h
+http-walker.s http-walker.o: ALL_CFLAGS += -DNO_EXPAT
 endif
 
 git-%$X: %.o $(GITLIBS)
@@ -1677,7 +1690,13 @@ git-http-push$X: revision.o http.o http-push.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
-git-remote-curl$X: remote-curl.o http.o http-walker.o $(GITLIBS)
+$(REMOTE_CURL_ALIASES): $(REMOTE_CURL_PRIMARY)
+       $(QUIET_LNCP)$(RM) $@ && \
+       ln $< $@ 2>/dev/null || \
+       ln -s $< $@ 2>/dev/null || \
+       cp $< $@
+
+$(REMOTE_CURL_PRIMARY): remote-curl.o http.o http-walker.o $(GITLIBS)
        $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
 
@@ -1728,7 +1747,7 @@ cscope:
 TRACK_CFLAGS = $(subst ','\'',$(ALL_CFLAGS)):\
              $(bindir_SQ):$(gitexecdir_SQ):$(template_dir_SQ):$(prefix_SQ)
 
-GIT-CFLAGS: .FORCE-GIT-CFLAGS
+GIT-CFLAGS: FORCE
        @FLAGS='$(TRACK_CFLAGS)'; \
            if test x"$$FLAGS" != x"`cat GIT-CFLAGS 2>/dev/null`" ; then \
                echo 1>&2 "    * new build flags or prefix"; \
@@ -1738,7 +1757,7 @@ GIT-CFLAGS: .FORCE-GIT-CFLAGS
 # We need to apply sq twice, once to protect from the shell
 # that runs GIT-BUILD-OPTIONS, and then again to protect it
 # and the first level quoting from the shell that runs "echo".
-GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
+GIT-BUILD-OPTIONS: FORCE
        @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
        @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
        @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
@@ -1750,31 +1769,41 @@ GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
 ifndef NO_TCLTK
 TRACK_VARS = $(subst ','\'',-DTCLTK_PATH='$(TCLTK_PATH_SQ)')
 
-GIT-GUI-VARS: .FORCE-GIT-GUI-VARS
+GIT-GUI-VARS: FORCE
        @VARS='$(TRACK_VARS)'; \
            if test x"$$VARS" != x"`cat $@ 2>/dev/null`" ; then \
                echo 1>&2 "    * new Tcl/Tk interpreter location"; \
                echo "$$VARS" >$@; \
             fi
-
-.PHONY: .FORCE-GIT-GUI-VARS
 endif
 
 ### Testing rules
 
-TEST_PROGRAMS += test-chmtime$X
-TEST_PROGRAMS += test-ctype$X
-TEST_PROGRAMS += test-date$X
-TEST_PROGRAMS += test-delta$X
-TEST_PROGRAMS += test-dump-cache-tree$X
-TEST_PROGRAMS += test-genrandom$X
-TEST_PROGRAMS += test-match-trees$X
-TEST_PROGRAMS += test-parse-options$X
-TEST_PROGRAMS += test-path-utils$X
-TEST_PROGRAMS += test-sha1$X
-TEST_PROGRAMS += test-sigchain$X
+TEST_PROGRAMS_NEED_X += test-chmtime
+TEST_PROGRAMS_NEED_X += test-ctype
+TEST_PROGRAMS_NEED_X += test-date
+TEST_PROGRAMS_NEED_X += test-delta
+TEST_PROGRAMS_NEED_X += test-dump-cache-tree
+TEST_PROGRAMS_NEED_X += test-genrandom
+TEST_PROGRAMS_NEED_X += test-match-trees
+TEST_PROGRAMS_NEED_X += test-parse-options
+TEST_PROGRAMS_NEED_X += test-path-utils
+TEST_PROGRAMS_NEED_X += test-sha1
+TEST_PROGRAMS_NEED_X += test-sigchain
+TEST_PROGRAMS_NEED_X += test-index-version
 
-all:: $(TEST_PROGRAMS)
+TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
+
+test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
+
+all:: $(TEST_PROGRAMS) $(test_bindir_programs)
+
+bin-wrappers/%: wrap-for-bin.sh
+       @mkdir -p bin-wrappers
+       $(QUIET_GEN)sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
+            -e 's|@@BUILD_DIR@@|$(shell pwd)|' \
+            -e 's|@@PROG@@|$(@F)|' < $< > $@ && \
+       chmod +x $@
 
 # GNU make supports exporting all variables by "export" without parameters.
 # However, the environment gets quite big, and some programs have problems
@@ -1835,11 +1864,13 @@ endif
 gitexec_instdir_SQ = $(subst ','\'',$(gitexec_instdir))
 export gitexec_instdir
 
+install_bindir_programs := $(patsubst %,%$X,$(BINDIR_PROGRAMS_NEED_X)) $(BINDIR_PROGRAMS_NO_X)
+
 install: all
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(bindir_SQ)'
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
        $(INSTALL) $(ALL_PROGRAMS) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
-       $(INSTALL) git$X git-upload-pack$X git-receive-pack$X git-upload-archive$X git-shell$X git-cvsserver '$(DESTDIR_SQ)$(bindir_SQ)'
+       $(INSTALL) $(install_bindir_programs) '$(DESTDIR_SQ)$(bindir_SQ)'
        $(MAKE) -C templates DESTDIR='$(DESTDIR_SQ)' install
 ifndef NO_PERL
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
@@ -1854,6 +1885,7 @@ endif
 ifneq (,$X)
        $(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), test '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p' -ef '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p$X' || $(RM) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)/$p';)
 endif
+
        bindir=$$(cd '$(DESTDIR_SQ)$(bindir_SQ)' && pwd) && \
        execdir=$$(cd '$(DESTDIR_SQ)$(gitexec_instdir_SQ)' && pwd) && \
        { test "$$bindir/" = "$$execdir/" || \
@@ -1867,6 +1899,12 @@ endif
                ln -s "git$X" "$$execdir/$$p" 2>/dev/null || \
                cp "$$execdir/git$X" "$$execdir/$$p" || exit; \
          done; } && \
+       { for p in $(REMOTE_CURL_ALIASES); do \
+               $(RM) "$$execdir/$$p" && \
+               ln "$$execdir/git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
+               ln -s "git-remote-http$X" "$$execdir/$$p" 2>/dev/null || \
+               cp "$$execdir/git-remote-http$X" "$$execdir/$$p" || exit; \
+         done; } && \
        ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
 
 install-doc:
@@ -1953,6 +1991,7 @@ clean:
                $(LIB_FILE) $(XDIFF_LIB)
        $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
+       $(RM) -r bin-wrappers
        $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags cscope*
        $(RM) -r autom4te.cache
        $(RM) config.log config.mak.autogen config.mak.append config.status config.cache
@@ -1977,8 +2016,7 @@ endif
 
 .PHONY: all install clean strip
 .PHONY: shell_compatibility_test please_set_SHELL_PATH_to_a_more_modern_shell
-.PHONY: .FORCE-GIT-VERSION-FILE TAGS tags cscope .FORCE-GIT-CFLAGS
-.PHONY: .FORCE-GIT-BUILD-OPTIONS
+.PHONY: FORCE TAGS tags cscope
 
 ### Check documentation
 #
index 55b273246e006ad55c51d3e5cb6bed3153ae8cf4..5b88507374f08e118a83fe7479766e078c5616ca 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -211,10 +211,33 @@ static const struct archiver *lookup_archiver(const char *name)
        return NULL;
 }
 
+static int reject_entry(const unsigned char *sha1, const char *base,
+                       int baselen, const char *filename, unsigned mode,
+                       int stage, void *context)
+{
+       return -1;
+}
+
+static int path_exists(struct tree *tree, const char *path)
+{
+       const char *pathspec[] = { path, NULL };
+
+       if (read_tree_recursive(tree, "", 0, 0, pathspec, reject_entry, NULL))
+               return 1;
+       return 0;
+}
+
 static void parse_pathspec_arg(const char **pathspec,
                struct archiver_args *ar_args)
 {
-       ar_args->pathspec = get_pathspec("", pathspec);
+       ar_args->pathspec = pathspec = get_pathspec("", pathspec);
+       if (pathspec) {
+               while (*pathspec) {
+                       if (!path_exists(ar_args->tree, *pathspec))
+                               die("path not found: %s", *pathspec);
+                       pathspec++;
+               }
+       }
 }
 
 static void parse_treeish_arg(const char **argv,
index b417a15bbc83fff7180078a4cf9f73603477a295..e459feebbf90c6557dbf3ff913f83a57a8afb210 100644 (file)
--- a/base85.c
+++ b/base85.c
@@ -57,14 +57,8 @@ int decode_85(char *dst, const char *buffer, int len)
                de = de85[ch];
                if (--de < 0)
                        return error("invalid base85 alphabet %c", ch);
-               /*
-                * Detect overflow.  The largest
-                * 5-letter possible is "|NsC0" to
-                * encode 0xffffffff, and "|NsC" gives
-                * 0x03030303 at this point (i.e.
-                * 0xffffffff = 0x03030303 * 85).
-                */
-               if (0x03030303 < acc ||
+               /* Detect overflow. */
+               if (0xffffffff / 85 < acc ||
                    0xffffffff - de < (acc *= 85))
                        return error("invalid base85 sequence %.5s", buffer-5);
                acc += de;
@@ -84,8 +78,6 @@ int decode_85(char *dst, const char *buffer, int len)
 
 void encode_85(char *buf, const unsigned char *data, int bytes)
 {
-       prep_base85();
-
        say("encode 85");
        while (bytes) {
                unsigned acc = 0;
@@ -118,7 +110,7 @@ int main(int ac, char **av)
                int len = strlen(av[2]);
                encode_85(buf, av[2], len);
                if (len <= 26) len = len + 'A' - 1;
-               else len = len + 'a' - 26 + 1;
+               else len = len + 'a' - 26 - 1;
                printf("encoded: %c%s\n", len, buf);
                return 0;
        }
index dc18db8af96b093d7cb16eb88d3f738f98dc30f6..6dc27ee7a6090e56d5b0f2072a72553d3b3e3b87 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -593,7 +593,7 @@ struct commit_list *filter_skipped(struct commit_list *list,
  * is increased by one between each call, but that should not matter
  * for this application.
  */
-int get_prn(int count) {
+static int get_prn(int count) {
        count = count * 1103515245 + 12345;
        return ((unsigned)(count/65536) % PRN_MODULO);
 }
@@ -813,11 +813,11 @@ static void handle_skipped_merge_base(const unsigned char *mb)
        char *bad_hex = sha1_to_hex(current_bad_sha1);
        char *good_hex = join_sha1_array_hex(&good_revs, ' ');
 
-       fprintf(stderr, "Warning: the merge base between %s and [%s] "
+       warning("the merge base between %s and [%s] "
                "must be skipped.\n"
                "So we cannot be sure the first bad commit is "
                "between %s and %s.\n"
-               "We continue anyway.\n",
+               "We continue anyway.",
                bad_hex, good_hex, mb_hex, bad_hex);
        free(good_hex);
 }
@@ -956,7 +956,7 @@ int bisect_next_all(const char *prefix)
 {
        struct rev_info revs;
        struct commit_list *tried;
-       int reaches = 0, all = 0, nr;
+       int reaches = 0, all = 0, nr, steps;
        const unsigned char *bisect_rev;
        char bisect_rev_hex[41];
 
@@ -998,8 +998,10 @@ int bisect_next_all(const char *prefix)
        }
 
        nr = all - reaches - 1;
-       printf("Bisecting: %d revisions left to test after this "
-              "(roughly %d steps)\n", nr, estimate_bisect_steps(all));
+       steps = estimate_bisect_steps(all);
+       printf("Bisecting: %d revision%s left to test after this "
+              "(roughly %d step%s)\n", nr, (nr == 1 ? "" : "s"),
+              steps, (steps == 1 ? "" : "s"));
 
        return bisect_checkout(bisect_rev_hex);
 }
index 82f8fc1910a42c5c90ad3312d9aa8f35ef2b992f..0862ce56d76e9b08ab913e6a472fac590974340e 100644 (file)
--- a/bisect.h
+++ b/bisect.h
@@ -27,8 +27,6 @@ struct rev_list_info {
        const char *header_prefix;
 };
 
-extern int show_bisect_vars(struct rev_list_info *info, int reaches, int all);
-
 extern int bisect_next_all(const char *prefix);
 
 extern int estimate_bisect_steps(int all);
diff --git a/blob.c b/blob.c
index bd7d078e1ae5fe4ce0a16fda62a2c1743237941b..ae320bd8fa22aaaef8144bd6bc35c20d1e85e4f4 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -23,24 +23,3 @@ int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
        item->object.parsed = 1;
        return 0;
 }
-
-int parse_blob(struct blob *item)
-{
-        enum object_type type;
-        void *buffer;
-        unsigned long size;
-       int ret;
-
-        if (item->object.parsed)
-                return 0;
-        buffer = read_sha1_file(item->object.sha1, &type, &size);
-        if (!buffer)
-                return error("Could not read %s",
-                             sha1_to_hex(item->object.sha1));
-        if (type != OBJ_BLOB)
-                return error("Object %s not a blob",
-                             sha1_to_hex(item->object.sha1));
-       ret = parse_blob_buffer(item, buffer, size);
-       free(buffer);
-       return ret;
-}
diff --git a/blob.h b/blob.h
index ea5d9e9f8b63be2c7048d19ee53feb06b0795c80..59b394eea38494d5dfa525e28ca949e5a03efcf5 100644 (file)
--- a/blob.h
+++ b/blob.h
@@ -13,6 +13,13 @@ struct blob *lookup_blob(const unsigned char *sha1);
 
 int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
 
-int parse_blob(struct blob *item);
+/**
+ * Blobs do not contain references to other objects and do not have
+ * structured data that needs parsing. However, code may use the
+ * "parsed" bit in the struct object for a blob to determine whether
+ * its content has been found to actually be available, so
+ * parse_blob_buffer() is used (by object.c) to flag that the object
+ * has been read successfully from the database.
+ **/
 
 #endif /* BLOB_H */
index 36e2f9dda5c85c346e31f45afa6d28b107679970..541493e1ba81462f4b01bb99c2840938bae02e4f 100644 (file)
@@ -2666,7 +2666,7 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st)
                        return -1;
                return 0;
        }
-       return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID);
+       return ce_match_stat(ce, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
 }
 
 static int check_preimage(struct patch *patch, struct cache_entry **ce, struct stat *st)
index 446d6bff30ed47a5cf308310e3f28c5b30648a2c..3fb41364a512db28c92d79a21c60b238141c7970 100644 (file)
@@ -5,6 +5,7 @@
 #include "cache.h"
 #include "builtin.h"
 #include "archive.h"
+#include "transport.h"
 #include "parse-options.h"
 #include "pkt-line.h"
 #include "sideband.h"
@@ -25,12 +26,16 @@ static void create_output_file(const char *output_file)
 static int run_remote_archiver(int argc, const char **argv,
                               const char *remote, const char *exec)
 {
-       char *url, buf[LARGE_PACKET_MAX];
+       char buf[LARGE_PACKET_MAX];
        int fd[2], i, len, rv;
-       struct child_process *conn;
+       struct transport *transport;
+       struct remote *_remote;
 
-       url = xstrdup(remote);
-       conn = git_connect(fd, url, exec, 0);
+       _remote = remote_get(remote);
+       if (!_remote->url[0])
+               die("git archive: Remote with no URL");
+       transport = transport_get(_remote, _remote->url[0]);
+       transport_connect(transport, "git-upload-archive", exec, fd);
 
        for (i = 1; i < argc; i++)
                packet_write(fd[1], "argument %s\n", argv[i]);
@@ -53,9 +58,7 @@ static int run_remote_archiver(int argc, const char **argv,
 
        /* Now, start reading from fd[0] and spit it out to stdout */
        rv = recv_sideband("archive", fd[0], 1);
-       close(fd[0]);
-       close(fd[1]);
-       rv |= finish_connect(conn);
+       rv |= transport_disconnect(transport);
 
        return !!rv;
 }
index c87e63b02dd3bb0e12e5bf14aec22a0f219867b0..ddc9f2dab7f3b55e4cd0cffbf303a3ad108fd431 100644 (file)
@@ -638,10 +638,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                rename_branch(head, argv[0], rename > 1);
        else if (rename && (argc == 2))
                rename_branch(argv[0], argv[1], rename > 1);
-       else if (argc <= 2)
+       else if (argc <= 2) {
+               if (kinds != REF_LOCAL_BRANCH)
+                       die("-a and -r options to 'git branch' do not make sense with a branch name");
                create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
                              force_create, reflog, track);
-       else
+       else
                usage_with_options(builtin_branch_usage, options);
 
        return 0;
index 64f3a11ae1d415c466def8eccefe9dbc5b6b3926..e44e238c39a7d4c276edf3bfe054b5929c009eda 100644 (file)
@@ -167,7 +167,7 @@ static int checkout_merged(int pos, struct checkout *state)
        fill_mm(active_cache[pos+2]->sha1, &theirs);
 
        status = ll_merge(&result_buf, path, &ancestor,
-                         &ours, "ours", &theirs, "theirs", 1);
+                         &ours, "ours", &theirs, "theirs", 0);
        free(ancestor.ptr);
        free(ours.ptr);
        free(theirs.ptr);
@@ -397,7 +397,7 @@ static int merge_working_tree(struct checkout_opts *opts,
                topts.initial_checkout = is_cache_unborn();
                topts.update = 1;
                topts.merge = 1;
-               topts.gently = opts->merge;
+               topts.gently = opts->merge && old->commit;
                topts.verbose_update = !opts->quiet;
                topts.fn = twoway_merge;
                topts.dir = xcalloc(1, sizeof(*topts.dir));
@@ -422,7 +422,13 @@ static int merge_working_tree(struct checkout_opts *opts,
                        struct merge_options o;
                        if (!opts->merge)
                                return 1;
-                       parse_commit(old->commit);
+
+                       /*
+                        * Without old->commit, the below is the same as
+                        * the two-tree unpack we already tried and failed.
+                        */
+                       if (!old->commit)
+                               return 1;
 
                        /* Do more real merge */
 
@@ -690,7 +696,10 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
         * case 3: git checkout <something> [<paths>]
         *
         *   With no paths, if <something> is a commit, that is to
-        *   switch to the branch or detach HEAD at it.
+        *   switch to the branch or detach HEAD at it.  As a special case,
+        *   if <something> is A...B (missing A or B means HEAD but you can
+        *   omit at most one side), and if there is a unique merge base
+        *   between A and B, A...B names that merge base.
         *
         *   With no paths, if <something> is _not_ a commit, no -t nor -b
         *   was given, and there is a tracking branch whose name is
@@ -716,7 +725,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                if (!strcmp(arg, "-"))
                        arg = "@{-1}";
 
-               if (get_sha1(arg, rev)) {
+               if (get_sha1_mb(arg, rev)) {
                        if (has_dash_dash)          /* case (1) */
                                die("invalid reference: %s", arg);
                        if (!patch_mode &&
index 28cdcd0274dd5891c7ad3dc3804cb0acead883f2..3a70fa81bdd8f0e087476560f7c2f668a3cda0ea 100644 (file)
@@ -75,11 +75,13 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
 
        dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
 
+       if (read_cache() < 0)
+               die("index file corrupt");
+
        if (!ignored)
                setup_standard_excludes(&dir);
 
        pathspec = get_pathspec(prefix, argv);
-       read_cache();
 
        fill_directory(&dir, pathspec);
 
index 5df8b0f72c8bb5dfa7fa7df1edfec90405031f47..58bacbd552c1e2496034346265a3e5ab219e2672 100644 (file)
@@ -44,10 +44,13 @@ static char *option_origin = NULL;
 static char *option_branch = NULL;
 static char *option_upload_pack = "git-upload-pack";
 static int option_verbose;
+static int option_progress;
 
 static struct option builtin_clone_options[] = {
        OPT__QUIET(&option_quiet),
        OPT__VERBOSE(&option_verbose),
+       OPT_BOOLEAN(0, "progress", &option_progress,
+                       "force progress reporting"),
        OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
                    "don't create a checkout"),
        OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"),
@@ -526,6 +529,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                if (option_quiet)
                        transport->verbose = -1;
                else if (option_verbose)
+                       transport->verbose = 1;
+
+               if (option_progress)
                        transport->progress = 1;
 
                if (option_upload_pack)
index 6822aa07ca80ee648fd331a79f54915cd338f8d2..69241f8ed6245b4f80f8758540bdd1aba926c4ce 100644 (file)
@@ -53,7 +53,7 @@ static char *edit_message, *use_message;
 static char *author_name, *author_email, *author_date;
 static int all, edit_flag, also, interactive, only, amend, signoff;
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
-static char *untracked_files_arg;
+static char *untracked_files_arg, *force_date;
 /*
  * The default commit message cleanup mode will remove the lines
  * beginning with # (shell comments) and leading and trailing
@@ -68,7 +68,7 @@ static enum {
 } cleanup_mode;
 static char *cleanup_arg;
 
-static int use_editor = 1, initial_commit, in_merge;
+static int use_editor = 1, initial_commit, in_merge, include_status = 1;
 static const char *only_include_assumed;
 static struct strbuf message;
 
@@ -94,10 +94,11 @@ static int opt_parse_m(const struct option *opt, const char *arg, int unset)
 static struct option builtin_commit_options[] = {
        OPT__QUIET(&quiet),
        OPT__VERBOSE(&verbose),
-       OPT_GROUP("Commit message options"),
 
+       OPT_GROUP("Commit message options"),
        OPT_FILENAME('F', "file", &logfile, "read log from file"),
        OPT_STRING(0, "author", &force_author, "AUTHOR", "override author for commit"),
+       OPT_STRING(0, "date", &force_date, "DATE", "override date for commit"),
        OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
        OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit"),
        OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
@@ -105,6 +106,9 @@ static struct option builtin_commit_options[] = {
        OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
        OPT_FILENAME('t', "template", &template_file, "use specified template file"),
        OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
+       OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
+       OPT_BOOLEAN(0, "status", &include_status, "include status in commit message template"),
+       /* end commit message options */
 
        OPT_GROUP("Commit contents options"),
        OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
@@ -122,7 +126,7 @@ static struct option builtin_commit_options[] = {
        OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
        { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, "mode", "show untracked files, optional modes: all, normal, no. (Default: all)", PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
        OPT_BOOLEAN(0, "allow-empty", &allow_empty, "ok to record an empty change"),
-       OPT_STRING(0, "cleanup", &cleanup_arg, "default", "how to strip spaces and #comments from message"),
+       /* end commit contents options */
 
        OPT_END()
 };
@@ -180,11 +184,15 @@ static int list_paths(struct string_list *list, const char *with_tree,
 
        for (i = 0; i < active_nr; i++) {
                struct cache_entry *ce = active_cache[i];
+               struct string_list_item *item;
+
                if (ce->ce_flags & CE_UPDATE)
                        continue;
                if (!match_pathspec(pattern, ce->name, ce_namelen(ce), 0, m))
                        continue;
-               string_list_insert(ce->name, list);
+               item = string_list_insert(ce->name, list);
+               if (ce_skip_worktree(ce))
+                       item->util = item; /* better a valid pointer than a fake one */
        }
 
        return report_path_error(m, pattern, prefix ? strlen(prefix) : 0);
@@ -197,6 +205,10 @@ static void add_remove_files(struct string_list *list)
                struct stat st;
                struct string_list_item *p = &(list->items[i]);
 
+               /* p->util is skip-worktree */
+               if (p->util)
+                       continue;
+
                if (!lstat(p->string, &st)) {
                        if (add_to_cache(p->string, &st, 0))
                                die("updating files failed");
@@ -439,6 +451,9 @@ static void determine_author_info(void)
                email = xstrndup(lb + 2, rb - (lb + 2));
        }
 
+       if (force_date)
+               date = force_date;
+
        author_name = name;
        author_email = email;
        author_date = date;
@@ -576,7 +591,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
 
        /* This checks if committer ident is explicitly given */
        git_committer_info(0);
-       if (use_editor) {
+       if (use_editor && include_status) {
                char *author_ident;
                const char *committer_ident;
 
@@ -1091,6 +1106,10 @@ static int git_commit_config(const char *k, const char *v, void *cb)
 
        if (!strcmp(k, "commit.template"))
                return git_config_pathname(&template_file, k, v);
+       if (!strcmp(k, "commit.status")) {
+               include_status = git_config_bool(k, v);
+               return 0;
+       }
 
        return git_status_config(k, v, s);
 }
@@ -1236,7 +1255,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                     "new_index file. Check that disk is not full or quota is\n"
                     "not exceeded, and then \"git reset HEAD\" to recover.");
 
-       rerere();
+       rerere(0);
        run_hook(get_index_file(), "post-commit", NULL);
        if (!quiet)
                print_summary(prefix, commit_sha1);
index a2d656edb383da47fb3622f1f7c2d1524285ac00..2e3ef911d6d6682bc2f71ea912b05768fb6298be 100644 (file)
@@ -45,6 +45,7 @@ static int end_null;
 #define TYPE_BOOL (1<<0)
 #define TYPE_INT (1<<1)
 #define TYPE_BOOL_OR_INT (1<<2)
+#define TYPE_PATH (1<<3)
 
 static struct option builtin_config_options[] = {
        OPT_GROUP("Config file location"),
@@ -69,6 +70,7 @@ static struct option builtin_config_options[] = {
        OPT_BIT(0, "bool", &types, "value is \"true\" or \"false\"", TYPE_BOOL),
        OPT_BIT(0, "int", &types, "value is decimal number", TYPE_INT),
        OPT_BIT(0, "bool-or-int", &types, "value is --bool or --int", TYPE_BOOL_OR_INT),
+       OPT_BIT(0, "path", &types, "value is a path (file or directory name)", TYPE_PATH),
        OPT_GROUP("Other"),
        OPT_BOOLEAN('z', "null", &end_null, "terminate values with NUL byte"),
        OPT_END(),
@@ -94,6 +96,7 @@ static int show_config(const char *key_, const char *value_, void *cb)
 {
        char value[256];
        const char *vptr = value;
+       int must_free_vptr = 0;
        int dup_error = 0;
 
        if (!use_key_regexp && strcmp(key_, key))
@@ -123,6 +126,9 @@ static int show_config(const char *key_, const char *value_, void *cb)
                        vptr = v ? "true" : "false";
                else
                        sprintf(value, "%d", v);
+       } else if (types == TYPE_PATH) {
+               git_config_pathname(&vptr, key_, value_);
+               must_free_vptr = 1;
        }
        else
                vptr = value_?value_:"";
@@ -133,6 +139,12 @@ static int show_config(const char *key_, const char *value_, void *cb)
        }
        else
                printf("%s%c", vptr, term);
+       if (must_free_vptr)
+               /* If vptr must be freed, it's a pointer to a
+                * dynamically allocated buffer, it's safe to cast to
+                * const.
+               */
+               free((char *)vptr);
 
        return 0;
 }
@@ -215,7 +227,13 @@ static char *normalize_value(const char *key, const char *value)
        if (!value)
                return NULL;
 
-       if (types == 0)
+       if (types == 0 || types == TYPE_PATH)
+               /*
+                * We don't do normalization for TYPE_PATH here: If
+                * the path is like ~/foobar/, we prefer to store
+                * "~/foobar/" in the config file, and to expand the ~
+                * when retrieving the value.
+                */
                normalized = xstrdup(value);
        else {
                normalized = xmalloc(64);
index 1b0b6c84ea4279df56b82c5a406adbea59e58513..2bdd8ebde1002e852055385396ef053f26c91ae8 100644 (file)
@@ -11,7 +11,7 @@
 
 static void count_objects(DIR *d, char *path, int len, int verbose,
                          unsigned long *loose,
-                         unsigned long *loose_size,
+                         off_t *loose_size,
                          unsigned long *packed_loose,
                          unsigned long *garbage)
 {
@@ -77,7 +77,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
        int len = strlen(objdir);
        char *path = xmalloc(len + 50);
        unsigned long loose = 0, packed = 0, packed_loose = 0, garbage = 0;
-       unsigned long loose_size = 0;
+       off_t loose_size = 0;
        struct option opts[] = {
                OPT__VERBOSE(&verbose),
                OPT_END(),
@@ -103,7 +103,7 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
        if (verbose) {
                struct packed_git *p;
                unsigned long num_pack = 0;
-               unsigned long size_pack = 0;
+               off_t size_pack = 0;
                if (!packed_git)
                        prepare_packed_git();
                for (p = packed_git; p; p = p->next) {
@@ -116,15 +116,15 @@ int cmd_count_objects(int argc, const char **argv, const char *prefix)
                        num_pack++;
                }
                printf("count: %lu\n", loose);
-               printf("size: %lu\n", loose_size / 1024);
+               printf("size: %lu\n", (unsigned long) (loose_size / 1024));
                printf("in-pack: %lu\n", packed);
                printf("packs: %lu\n", num_pack);
-               printf("size-pack: %lu\n", size_pack / 1024);
+               printf("size-pack: %lu\n", (unsigned long) (size_pack / 1024));
                printf("prune-packable: %lu\n", packed_loose);
                printf("garbage: %lu\n", garbage);
        }
        else
                printf("%lu objects, %lu kilobytes\n",
-                      loose, loose_size / 1024);
+                      loose, (unsigned long) (loose_size / 1024));
        return 0;
 }
index 093517e390044055039b9f4e58132c73d17b741d..c304638b7845601f184149d3c48cbc1ea1e195f5 100644 (file)
@@ -180,12 +180,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        char buf[80];
 
        struct option builtin_gc_options[] = {
+               OPT__QUIET(&quiet),
                { OPTION_STRING, 0, "prune", &prune_expire, "date",
                        "prune unreferenced objects",
                        PARSE_OPT_OPTARG, NULL, (intptr_t)prune_expire },
                OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
                OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
-               OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"),
                OPT_END()
        };
 
index a5b6719a1af014497399ea6ff4f1a6f3852a0570..cea973ba6bafff69b72db0923e5b583dc0a3f8bc 100644 (file)
@@ -191,8 +191,6 @@ static int grep_file(struct grep_opt *opt, const char *filename)
                        error("'%s': %s", filename, strerror(errno));
                return 0;
        }
-       if (!st.st_size)
-               return 0; /* empty file -- no grep hit */
        if (!S_ISREG(st.st_mode))
                return 0;
        sz = xsize_t(st.st_size);
@@ -207,6 +205,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
                return 0;
        }
        close(i);
+       data[sz] = 0;
        if (opt->relative && opt->prefix_length)
                filename = quote_path_relative(filename, -1, &buf, opt->prefix);
        i = grep_buffer(opt, filename, data, sz);
@@ -222,6 +221,7 @@ static int exec_grep(int argc, const char **argv)
        int status;
 
        argv[argc] = NULL;
+       trace_argv_printf(argv, "trace: grep:");
        pid = fork();
        if (pid < 0)
                return pid;
@@ -347,6 +347,21 @@ static void grep_add_color(struct strbuf *sb, const char *escape_seq)
                strbuf_setlen(sb, sb->len - 1);
 }
 
+static int has_skip_worktree_entry(struct grep_opt *opt, const char **paths)
+{
+       int nr;
+       for (nr = 0; nr < active_nr; nr++) {
+               struct cache_entry *ce = active_cache[nr];
+               if (!S_ISREG(ce->ce_mode))
+                       continue;
+               if (!pathspec_matches(paths, ce->name, opt->max_depth))
+                       continue;
+               if (ce_skip_worktree(ce))
+                       return 1;
+       }
+       return 0;
+}
+
 static int external_grep(struct grep_opt *opt, const char **paths, int cached)
 {
        int i, nr, argc, hit, len, status;
@@ -355,7 +370,8 @@ static int external_grep(struct grep_opt *opt, const char **paths, int cached)
        char *argptr = randarg;
        struct grep_pat *p;
 
-       if (opt->extended || (opt->relative && opt->prefix_length))
+       if (opt->extended || (opt->relative && opt->prefix_length)
+           || has_skip_worktree_entry(opt, paths))
                return -1;
        len = nr = 0;
        push_arg("grep");
@@ -512,7 +528,7 @@ static int grep_cache(struct grep_opt *opt, const char **paths, int cached,
                 * are identical, even if worktree file has been modified, so use
                 * cache version instead
                 */
-               if (cached || (ce->ce_flags & CE_VALID)) {
+               if (cached || (ce->ce_flags & CE_VALID) || ce_skip_worktree(ce)) {
                        if (ce_stage(ce))
                                continue;
                        hit |= grep_sha1(opt, ce->sha1, ce->name, 0);
index 09ad4b04f9fa860a32580717e61acc942de23388..3182a2bec466c50a9a1db1e91888bad3335414b1 100644 (file)
@@ -23,13 +23,14 @@ static struct man_viewer_info_list {
 } *man_viewer_info_list;
 
 enum help_format {
+       HELP_FORMAT_NONE,
        HELP_FORMAT_MAN,
        HELP_FORMAT_INFO,
        HELP_FORMAT_WEB,
 };
 
 static int show_all = 0;
-static enum help_format help_format = HELP_FORMAT_MAN;
+static enum help_format help_format = HELP_FORMAT_NONE;
 static struct option builtin_help_options[] = {
        OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
        OPT_SET_INT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
@@ -415,10 +416,12 @@ int cmd_help(int argc, const char **argv, const char *prefix)
 {
        int nongit;
        const char *alias;
+       enum help_format parsed_help_format;
        load_command_list("git-", &main_cmds, &other_cmds);
 
        argc = parse_options(argc, argv, prefix, builtin_help_options,
                        builtin_help_usage, 0);
+       parsed_help_format = help_format;
 
        if (show_all) {
                printf("usage: %s\n\n", git_usage_string);
@@ -437,6 +440,9 @@ int cmd_help(int argc, const char **argv, const char *prefix)
        setup_git_directory_gently(&nongit);
        git_config(git_help_config, NULL);
 
+       if (parsed_help_format != HELP_FORMAT_NONE)
+               help_format = parsed_help_format;
+
        alias = alias_lookup(argv[0]);
        if (alias && !is_git_command(argv[0])) {
                printf("`git %s' is aliased to `%s'\n", argv[0], alias);
@@ -444,6 +450,7 @@ int cmd_help(int argc, const char **argv, const char *prefix)
        }
 
        switch (help_format) {
+       case HELP_FORMAT_NONE:
        case HELP_FORMAT_MAN:
                show_man_page(argv[0]);
                break;
index c9a03e5427bbf1390aeefb9d48b4e362ca6a8e1b..738215768ea701f0a35ed1e7bdc543779e6f4185 100644 (file)
@@ -37,6 +37,7 @@ static const char *tag_removed = "";
 static const char *tag_other = "";
 static const char *tag_killed = "";
 static const char *tag_modified = "";
+static const char *tag_skip_worktree = "";
 
 static void show_dir_entry(const char *tag, struct dir_entry *ent)
 {
@@ -178,7 +179,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)
                                continue;
                        if (ce->ce_flags & CE_UPDATE)
                                continue;
-                       show_ce_entry(ce_stage(ce) ? tag_unmerged : tag_cached, ce);
+                       show_ce_entry(ce_stage(ce) ? tag_unmerged :
+                               (ce_skip_worktree(ce) ? tag_skip_worktree : tag_cached), ce);
                }
        }
        if (show_deleted | show_modified) {
@@ -192,6 +194,8 @@ static void show_files(struct dir_struct *dir, const char *prefix)
                                continue;
                        if (ce->ce_flags & CE_UPDATE)
                                continue;
+                       if (ce_skip_worktree(ce))
+                               continue;
                        err = lstat(ce->name, &st);
                        if (show_deleted && err)
                                show_ce_entry(tag_removed, ce);
@@ -481,6 +485,9 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
                prefix_offset = strlen(prefix);
        git_config(git_default_config, NULL);
 
+       if (read_cache() < 0)
+               die("index file corrupt");
+
        argc = parse_options(argc, argv, prefix, builtin_ls_files_options,
                        ls_files_usage, 0);
        if (show_tag || show_valid_bit) {
@@ -490,6 +497,7 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
                tag_modified = "C ";
                tag_other = "? ";
                tag_killed = "K ";
+               tag_skip_worktree = "S ";
        }
        if (show_modified || show_others || show_deleted || (dir.flags & DIR_SHOW_IGNORED) || show_killed)
                require_work_tree = 1;
@@ -508,7 +516,6 @@ int cmd_ls_files(int argc, const char **argv, const char *prefix)
        pathspec = get_pathspec(prefix, argv);
 
        /* be nice with submodule paths ending in a slash */
-       read_cache();
        if (pathspec)
                strip_trailing_slash_from_submodules();
 
index f1c84d759dd44f661fb76741d528e43702dfc901..82e2a0491a0f3f148ff65851d44481d2809f64de 100644 (file)
@@ -52,6 +52,7 @@ static struct strategy **use_strategies;
 static size_t use_strategies_nr, use_strategies_alloc;
 static const char *branch;
 static int verbosity;
+static int allow_rerere_auto;
 
 static struct strategy all_strategy[] = {
        { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -170,6 +171,7 @@ static struct option builtin_merge_options[] = {
                "allow fast-forward (default)"),
        OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
                "abort if fast-forward is not possible"),
+       OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
        OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
                "merge strategy to use", option_parse_strategy),
        OPT_CALLBACK('m', "message", &merge_msg, "message",
@@ -790,7 +792,7 @@ static int suggest_conflicts(void)
                }
        }
        fclose(fp);
-       rerere();
+       rerere(allow_rerere_auto);
        printf("Automatic merge failed; "
                        "fix conflicts and then commit the result.\n");
        return 1;
index f633d81424f5e41cb85ac660dbdc9f4913852673..82471869a0b677202fb5585e2fca880d16478af8 100644 (file)
@@ -169,9 +169,7 @@ int cmd_mv(int argc, const char **argv, const char *prefix)
                                 * check both source and destination
                                 */
                                if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)) {
-                                       fprintf(stderr, "Warning: %s;"
-                                                       " will overwrite!\n",
-                                                       bad);
+                                       warning("%s; will overwrite!", bad);
                                        bad = NULL;
                                } else
                                        bad = "Cannot overwrite";
index 4429d53a1e82b81f0a82e34bf2d6ae463aeeadee..890f45cf2070a476c0325033e898e9345091fef1 100644 (file)
@@ -1256,15 +1256,15 @@ static int delta_cacheable(unsigned long src_size, unsigned long trg_size,
 
 #ifdef THREADED_DELTA_SEARCH
 
-static pthread_mutex_t read_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t read_mutex;
 #define read_lock()            pthread_mutex_lock(&read_mutex)
 #define read_unlock()          pthread_mutex_unlock(&read_mutex)
 
-static pthread_mutex_t cache_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t cache_mutex;
 #define cache_lock()           pthread_mutex_lock(&cache_mutex)
 #define cache_unlock()         pthread_mutex_unlock(&cache_mutex)
 
-static pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t progress_mutex;
 #define progress_lock()                pthread_mutex_lock(&progress_mutex)
 #define progress_unlock()      pthread_mutex_unlock(&progress_mutex)
 
@@ -1591,7 +1591,26 @@ struct thread_params {
        unsigned *processed;
 };
 
-static pthread_cond_t progress_cond = PTHREAD_COND_INITIALIZER;
+static pthread_cond_t progress_cond;
+
+/*
+ * Mutex and conditional variable can't be statically-initialized on Windows.
+ */
+static void init_threaded_search(void)
+{
+       pthread_mutex_init(&read_mutex, NULL);
+       pthread_mutex_init(&cache_mutex, NULL);
+       pthread_mutex_init(&progress_mutex, NULL);
+       pthread_cond_init(&progress_cond, NULL);
+}
+
+static void cleanup_threaded_search(void)
+{
+       pthread_cond_destroy(&progress_cond);
+       pthread_mutex_destroy(&read_mutex);
+       pthread_mutex_destroy(&cache_mutex);
+       pthread_mutex_destroy(&progress_mutex);
+}
 
 static void *threaded_find_deltas(void *arg)
 {
@@ -1630,10 +1649,13 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
        struct thread_params *p;
        int i, ret, active_threads = 0;
 
+       init_threaded_search();
+
        if (!delta_search_threads)      /* --threads=0 means autodetect */
                delta_search_threads = online_cpus();
        if (delta_search_threads <= 1) {
                find_deltas(list, &list_size, window, depth, processed);
+               cleanup_threaded_search();
                return;
        }
        if (progress > pack_to_stdout)
@@ -1748,6 +1770,7 @@ static void ll_find_deltas(struct object_entry **list, unsigned list_size,
                        active_threads--;
                }
        }
+       cleanup_threaded_search();
        free(p);
 }
 
index dcfb53f1884d2648993214b4378afbd1f83760b0..28a26e7db2a86bf1514cdd54f9735c0be146dce7 100644 (file)
@@ -15,6 +15,7 @@ static const char * const push_usage[] = {
 };
 
 static int thin;
+static int deleterefs;
 static const char *receivepack;
 
 static const char **refspec;
@@ -39,11 +40,24 @@ static void set_refspecs(const char **refs, int nr)
                        if (nr <= ++i)
                                die("tag shorthand without <tag>");
                        len = strlen(refs[i]) + 11;
-                       tag = xmalloc(len);
-                       strcpy(tag, "refs/tags/");
+                       if (deleterefs) {
+                               tag = xmalloc(len+1);
+                               strcpy(tag, ":refs/tags/");
+                       } else {
+                               tag = xmalloc(len);
+                               strcpy(tag, "refs/tags/");
+                       }
                        strcat(tag, refs[i]);
                        ref = tag;
-               }
+               } else if (deleterefs && !strchr(ref, ':')) {
+                       char *delref;
+                       int len = strlen(ref)+1;
+                       delref = xmalloc(len);
+                       strcpy(delref, ":");
+                       strcat(delref, ref);
+                       ref = delref;
+               } else if (deleterefs)
+                       die("--delete only accepts plain target ref names");
                add_refspec(ref);
        }
 }
@@ -111,8 +125,8 @@ static int push_with_options(struct transport *transport, int flags)
 
        if (nonfastforward && advice_push_nonfastforward) {
                printf("To prevent you from losing history, non-fast-forward updates were rejected\n"
-                      "Merge the remote changes before pushing again.  See the 'non-fast-forward'\n"
-                      "section of 'git push --help' for details.\n");
+                      "Merge the remote changes before pushing again.  See the 'Note about\n"
+                      "fast-forwards' section of 'git push --help' for details.\n");
        }
 
        return 1;
@@ -196,6 +210,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT_BIT( 0 , "all", &flags, "push all refs", TRANSPORT_PUSH_ALL),
                OPT_BIT( 0 , "mirror", &flags, "mirror all refs",
                            (TRANSPORT_PUSH_MIRROR|TRANSPORT_PUSH_FORCE)),
+               OPT_BOOLEAN( 0, "delete", &deleterefs, "delete refs"),
                OPT_BOOLEAN( 0 , "tags", &tags, "push tags (can't be used with --all or --mirror)"),
                OPT_BIT('n' , "dry-run", &flags, "dry run", TRANSPORT_PUSH_DRY_RUN),
                OPT_BIT( 0,  "porcelain", &flags, "machine-readable output", TRANSPORT_PUSH_PORCELAIN),
@@ -209,6 +224,11 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        git_config(git_default_config, NULL);
        argc = parse_options(argc, argv, prefix, options, push_usage, 0);
 
+       if (deleterefs && (tags || (flags & (TRANSPORT_PUSH_ALL | TRANSPORT_PUSH_MIRROR))))
+               die("--delete is incompatible with --all, --mirror and --tags");
+       if (deleterefs && argc < 2)
+               die("--delete doesn't make sense without any refs");
+
        if (tags)
                add_refspec("refs/tags/*");
 
index 2a3a32cbfe83a0e0366d04a99bb49606f647f594..50413ca17df294a8b10dd406db55a24cf601bb55 100644 (file)
@@ -31,7 +31,7 @@ static int list_tree(unsigned char *sha1)
 }
 
 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>]]",
+       "git read-tree [[-m [--trivial] [--aggressive] | --reset | --prefix=<prefix>] [-u [--exclude-per-directory=<gitignore>] | -i]] [--no-sparse-checkout] [--index-output=<file>] <tree-ish1> [<tree-ish2> [<tree-ish3>]]",
        NULL
 };
 
@@ -98,6 +98,8 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                  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_SET_INT(0, "no-sparse-checkout", &opts.skip_sparse_checkout,
+                           "skip applying sparse checkout filter", 1),
                OPT_END()
        };
 
index a5019397ff840204963e4e4b23d2ca16de1a332a..c4945b870882834fdaa75fd3d65f664570f6a5da 100644 (file)
@@ -1238,13 +1238,11 @@ static int update(int argc, const char **argv)
                fetch_argv[fetch_argc++] = "--prune";
        if (verbose)
                fetch_argv[fetch_argc++] = "-v";
-       if (argc < 2) {
+       fetch_argv[fetch_argc++] = "--multiple";
+       if (argc < 2)
                fetch_argv[fetch_argc++] = "default";
-       } else {
-               fetch_argv[fetch_argc++] = "--multiple";
-               for (i = 1; i < argc; i++)
-                       fetch_argv[fetch_argc++] = argv[i];
-       }
+       for (i = 1; i < argc; i++)
+               fetch_argv[fetch_argc++] = argv[i];
 
        if (strcmp(fetch_argv[fetch_argc-1], "default") == 0) {
                git_config(get_remote_default, &default_defined);
index 2be9ffb77b38ae687537f3599c9d11e3fbc026a9..5028138898bf0d95d969ea19b85d23af129912d0 100644 (file)
@@ -103,15 +103,24 @@ static int diff_two(const char *file1, const char *label1,
 int cmd_rerere(int argc, const char **argv, const char *prefix)
 {
        struct string_list merge_rr = { NULL, 0, 0, 1 };
-       int i, fd;
-
+       int i, fd, flags = 0;
+
+       if (2 < argc) {
+               if (!strcmp(argv[1], "-h"))
+                       usage(git_rerere_usage);
+               if (!strcmp(argv[1], "--rerere-autoupdate"))
+                       flags = RERERE_AUTOUPDATE;
+               else if (!strcmp(argv[1], "--no-rerere-autoupdate"))
+                       flags = RERERE_NOAUTOUPDATE;
+               if (flags) {
+                       argc--;
+                       argv++;
+               }
+       }
        if (argc < 2)
-               return rerere();
-
-       if (!strcmp(argv[1], "-h"))
-               usage(git_rerere_usage);
+               return rerere(flags);
 
-       fd = setup_rerere(&merge_rr);
+       fd = setup_rerere(&merge_rr, flags);
        if (fd < 0)
                return 0;
 
index 11d1c6e4d6d2ec0580cae3725f6b3a093e68aa67..0f5022eed24f980f6fedee49f8602fefa6fe85e4 100644 (file)
@@ -18,6 +18,8 @@
 #include "tree.h"
 #include "branch.h"
 #include "parse-options.h"
+#include "unpack-trees.h"
+#include "cache-tree.h"
 
 static const char * const git_reset_usage[] = {
        "git reset [--mixed | --soft | --hard | --merge] [-q] [<commit>]",
@@ -54,27 +56,44 @@ static inline int is_merge(void)
 
 static int reset_index_file(const unsigned char *sha1, int reset_type, int quiet)
 {
-       int i = 0;
-       const char *args[6];
+       int nr = 1;
+       int newfd;
+       struct tree_desc desc[2];
+       struct unpack_trees_options opts;
+       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
 
-       args[i++] = "read-tree";
+       memset(&opts, 0, sizeof(opts));
+       opts.head_idx = 1;
+       opts.src_index = &the_index;
+       opts.dst_index = &the_index;
+       opts.fn = oneway_merge;
+       opts.merge = 1;
        if (!quiet)
-               args[i++] = "-v";
+               opts.verbose_update = 1;
        switch (reset_type) {
        case MERGE:
-               args[i++] = "-u";
-               args[i++] = "-m";
+               opts.update = 1;
                break;
        case HARD:
-               args[i++] = "-u";
+               opts.update = 1;
                /* fallthrough */
        default:
-               args[i++] = "--reset";
+               opts.reset = 1;
        }
-       args[i++] = sha1_to_hex(sha1);
-       args[i] = NULL;
 
-       return run_command_v_opt(args, RUN_GIT_CMD);
+       newfd = hold_locked_index(lock, 1);
+
+       read_cache_unmerged();
+
+       if (!fill_tree_descriptor(desc + nr - 1, sha1))
+               return error("Failed to find tree of %s.", sha1_to_hex(sha1));
+       if (unpack_trees(nr, desc, &opts))
+               return -1;
+       if (write_cache(newfd, active_cache, active_nr) ||
+           commit_locked_index(lock))
+               return error("Could not write new index file.");
+
+       return 0;
 }
 
 static void print_new_head_line(struct commit *commit)
@@ -202,6 +221,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        struct commit *commit;
        char *reflog_action, msg[1024];
        const struct option options[] = {
+               OPT__QUIET(&quiet),
                OPT_SET_INT(0, "mixed", &reset_type,
                                                "reset HEAD and index", MIXED),
                OPT_SET_INT(0, "soft", &reset_type, "reset only HEAD", SOFT),
@@ -209,8 +229,6 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                                "reset HEAD, index and working tree", HARD),
                OPT_SET_INT(0, "merge", &reset_type,
                                "reset HEAD, index and working tree", MERGE),
-               OPT_BOOLEAN('q', NULL, &quiet,
-                               "disable showing new HEAD in hard reset and progress message"),
                OPT_BOOLEAN('p', "patch", &patch_mode, "select hunks interactively"),
                OPT_END()
        };
@@ -286,9 +304,11 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        if (reset_type == NONE)
                reset_type = MIXED; /* by default */
 
-       if ((reset_type == HARD || reset_type == MERGE)
-           && !is_inside_work_tree())
-               die("%s reset requires a work tree",
+       if (reset_type == HARD || reset_type == MERGE)
+               setup_work_tree();
+
+       if (reset_type == MIXED && is_bare_repository())
+               die("%s reset is not allowed in a bare repository",
                    reset_type_names[reset_type]);
 
        /* Soft reset does not touch the index file nor the working tree
index cd97ded4d249b3beb5179150a283e8be43b9ba44..c924b3a2c76c1f9a7f5531504825ed1b5456d41a 100644 (file)
@@ -253,7 +253,7 @@ static void print_var_int(const char *var, int val)
        printf("%s=%d\n", var, val);
 }
 
-int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
+static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
 {
        int cnt, flags = info->bisect_show_flags;
        char hex[41] = "";
index 37d02335212dea9672e2472970f66dc4ac95dfd1..cbe5b428ad23708338dc7b11cb0105f53b045527 100644 (file)
@@ -581,6 +581,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_remote_ref(show_reference, NULL);
                                continue;
                        }
+                       if (!strcmp(arg, "--show-toplevel")) {
+                               const char *work_tree = get_git_work_tree();
+                               if (work_tree)
+                                       puts(work_tree);
+                               continue;
+                       }
                        if (!strcmp(arg, "--show-prefix")) {
                                if (prefix)
                                        puts(prefix);
index 151aa6a981832954120359f7c953015525b530d8..857ca2eefa9719029faf48a143ee4afa846c77a6 100644 (file)
@@ -38,6 +38,7 @@ static const char * const cherry_pick_usage[] = {
 static int edit, no_replay, no_commit, mainline, signoff;
 static enum { REVERT, CHERRY_PICK } action;
 static struct commit *commit;
+static int allow_rerere_auto;
 
 static const char *me;
 
@@ -57,6 +58,7 @@ static void parse_args(int argc, const char **argv)
                OPT_BOOLEAN('r', NULL, &noop, "no-op (backward compatibility)"),
                OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
                OPT_INTEGER('m', "mainline", &mainline, "parent number"),
+               OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
                OPT_END(),
        };
 
@@ -395,7 +397,7 @@ static int revert_or_cherry_pick(int argc, const char **argv)
                        die ("Error wrapping up %s", defmsg);
                fprintf(stderr, "Automatic %s failed.%s\n",
                        me, help_msg(commit->object.sha1));
-               rerere();
+               rerere(allow_rerere_auto);
                exit(1);
        }
        if (commit_lock_file(&msg_file) < 0)
index c4790185ebb9979a6772cf7159d16c2a1deeeaf3..4ef1c4f508b0261e725c360e96f2b8cbed50e9ce 100644 (file)
@@ -140,7 +140,7 @@ static int delete_tag(const char *name, const char *ref,
 {
        if (delete_ref(ref, sha1, 0))
                return 1;
-       printf("Deleted tag '%s'\n", name);
+       printf("Deleted tag '%s' (was %s)\n", name, find_unique_abbrev(sha1, DEFAULT_ABBREV));
        return 0;
 }
 
@@ -479,6 +479,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                die("%s: cannot lock the ref", ref);
        if (write_ref_sha1(lock, object, NULL) < 0)
                die("%s: cannot update the ref", ref);
+       if (force && hashcmp(prev, object))
+               printf("Updated tag '%s' (was %s)\n", tag, find_unique_abbrev(prev, DEFAULT_ABBREV));
 
        strbuf_release(&buf);
        return 0;
index a6b7f2d6361fc1e8ea9713082affc72362f08d76..a64a752990b85b69590cdbeed3901154642007b8 100644 (file)
@@ -24,8 +24,9 @@ static int info_only;
 static int force_remove;
 static int verbose;
 static int mark_valid_only;
-#define MARK_VALID 1
-#define UNMARK_VALID 2
+static int mark_skip_worktree_only;
+#define MARK_FLAG 1
+#define UNMARK_FLAG 2
 
 __attribute__((format (printf, 1, 2)))
 static void report(const char *fmt, ...)
@@ -41,19 +42,15 @@ static void report(const char *fmt, ...)
        va_end(vp);
 }
 
-static int mark_valid(const char *path)
+static int mark_ce_flags(const char *path, int flag, int mark)
 {
        int namelen = strlen(path);
        int pos = cache_name_pos(path, namelen);
        if (0 <= pos) {
-               switch (mark_valid_only) {
-               case MARK_VALID:
-                       active_cache[pos]->ce_flags |= CE_VALID;
-                       break;
-               case UNMARK_VALID:
-                       active_cache[pos]->ce_flags &= ~CE_VALID;
-                       break;
-               }
+               if (mark)
+                       active_cache[pos]->ce_flags |= flag;
+               else
+                       active_cache[pos]->ce_flags &= ~flag;
                cache_tree_invalidate_path(active_cache_tree, path);
                active_cache_changed = 1;
                return 0;
@@ -176,29 +173,29 @@ static int process_directory(const char *path, int len, struct stat *st)
        return error("%s: is a directory - add files inside instead", path);
 }
 
-/*
- * Process a regular file
- */
-static int process_file(const char *path, int len, struct stat *st)
-{
-       int pos = cache_name_pos(path, len);
-       struct cache_entry *ce = pos < 0 ? NULL : active_cache[pos];
-
-       if (ce && S_ISGITLINK(ce->ce_mode))
-               return error("%s is already a gitlink, not replacing", path);
-
-       return add_one_path(ce, path, len, st);
-}
-
 static int process_path(const char *path)
 {
-       int len;
+       int pos, len;
        struct stat st;
+       struct cache_entry *ce;
 
        len = strlen(path);
        if (has_symlink_leading_path(path, len))
                return error("'%s' is beyond a symbolic link", path);
 
+       pos = cache_name_pos(path, len);
+       ce = pos < 0 ? NULL : active_cache[pos];
+       if (ce && ce_skip_worktree(ce)) {
+               /*
+                * working directory version is assumed "good"
+                * so updating it does not make sense.
+                * On the other hand, removing it from index should work
+                */
+               if (allow_remove && remove_file_from_cache(path))
+                       return error("%s: cannot remove from the index", path);
+               return 0;
+       }
+
        /*
         * First things first: get the stat information, to decide
         * what to do about the pathname!
@@ -209,7 +206,13 @@ static int process_path(const char *path)
        if (S_ISDIR(st.st_mode))
                return process_directory(path, len, &st);
 
-       return process_file(path, len, &st);
+       /*
+        * Process a regular file
+        */
+       if (ce && S_ISGITLINK(ce->ce_mode))
+               return error("%s is already a gitlink, not replacing", path);
+
+       return add_one_path(ce, path, len, &st);
 }
 
 static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
@@ -277,7 +280,12 @@ static void update_one(const char *path, const char *prefix, int prefix_length)
                goto free_return;
        }
        if (mark_valid_only) {
-               if (mark_valid(p))
+               if (mark_ce_flags(p, CE_VALID, mark_valid_only == MARK_FLAG))
+                       die("Unable to mark file %s", path);
+               goto free_return;
+       }
+       if (mark_skip_worktree_only) {
+               if (mark_ce_flags(p, CE_SKIP_WORKTREE, mark_skip_worktree_only == MARK_FLAG))
                        die("Unable to mark file %s", path);
                goto free_return;
        }
@@ -389,7 +397,7 @@ static void read_index_info(int line_termination)
 }
 
 static const char update_index_usage[] =
-"git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] <file>...";
+"git update-index [-q] [--add] [--replace] [--remove] [--unmerged] [--refresh] [--really-refresh] [--cacheinfo] [--chmod=(+|-)x] [--assume-unchanged] [--skip-worktree|--no-skip-worktree] [--info-only] [--force-remove] [--stdin] [--index-info] [--unresolve] [--again | -g] [--ignore-missing] [-z] [--verbose] [--] <file>...";
 
 static unsigned char head_sha1[20];
 static unsigned char merge_head_sha1[20];
@@ -648,11 +656,19 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!strcmp(path, "--assume-unchanged")) {
-                               mark_valid_only = MARK_VALID;
+                               mark_valid_only = MARK_FLAG;
                                continue;
                        }
                        if (!strcmp(path, "--no-assume-unchanged")) {
-                               mark_valid_only = UNMARK_VALID;
+                               mark_valid_only = UNMARK_FLAG;
+                               continue;
+                       }
+                       if (!strcmp(path, "--no-skip-worktree")) {
+                               mark_skip_worktree_only = UNMARK_FLAG;
+                               continue;
+                       }
+                       if (!strcmp(path, "--skip-worktree")) {
+                               mark_skip_worktree_only = MARK_FLAG;
                                continue;
                        }
                        if (!strcmp(path, "--info-only")) {
diff --git a/cache.h b/cache.h
index bf468e52352c193b355222b718d9f5125c26052c..caeafb229245afe5be081684094ef81f6a6a8b6b 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -177,15 +177,20 @@ struct cache_entry {
 
 #define CE_HASHED    (0x100000)
 #define CE_UNHASHED  (0x200000)
+#define CE_CONFLICTED (0x800000)
+
+/* Only remove in work directory, not index */
+#define CE_WT_REMOVE (0x400000)
 
 /*
  * Extended on-disk flags
  */
 #define CE_INTENT_TO_ADD 0x20000000
+#define CE_SKIP_WORKTREE 0x40000000
 /* CE_EXTENDED2 is for future extension */
 #define CE_EXTENDED2 0x80000000
 
-#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD)
+#define CE_EXTENDED_FLAGS (CE_INTENT_TO_ADD | CE_SKIP_WORKTREE)
 
 /*
  * Safeguard to avoid saving wrong flags:
@@ -234,6 +239,7 @@ static inline size_t ce_namelen(const struct cache_entry *ce)
                            ondisk_cache_entry_size(ce_namelen(ce)))
 #define ce_stage(ce) ((CE_STAGEMASK & (ce)->ce_flags) >> CE_STAGESHIFT)
 #define ce_uptodate(ce) ((ce)->ce_flags & CE_UPTODATE)
+#define ce_skip_worktree(ce) ((ce)->ce_flags & CE_SKIP_WORKTREE)
 #define ce_mark_uptodate(ce) ((ce)->ce_flags |= CE_UPTODATE)
 
 #define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
@@ -445,7 +451,6 @@ extern int index_name_pos(const struct index_state *, const char *name, int name
 #define ADD_CACHE_JUST_APPEND 8                /* Append only; tree.c::read_tree() */
 #define ADD_CACHE_NEW_ONLY 16          /* Do not replace existing ones */
 extern int add_index_entry(struct index_state *, struct cache_entry *ce, int option);
-extern struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
 extern void rename_index_entry_at(struct index_state *, int pos, const char *new_name);
 extern int remove_index_entry_at(struct index_state *, int pos);
 extern void remove_marked_cache_entries(struct index_state *istate);
@@ -464,7 +469,9 @@ extern int index_name_is_other(const struct index_state *, const char *, int);
 /* do stat comparison even if CE_VALID is true */
 #define CE_MATCH_IGNORE_VALID          01
 /* do not check the contents but report dirty on racily-clean entries */
-#define CE_MATCH_RACY_IS_DIRTY 02
+#define CE_MATCH_RACY_IS_DIRTY         02
+/* do stat comparison even if CE_SKIP_WORKTREE is true */
+#define CE_MATCH_IGNORE_SKIP_WORKTREE  04
 extern int ie_match_stat(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
 extern int ie_modified(const struct index_state *, struct cache_entry *, struct stat *, unsigned int);
 
@@ -473,9 +480,6 @@ extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_obje
 extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
 extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 
-/* "careful lstat()" */
-extern int check_path(const char *path, int len, struct stat *st, int skiplen);
-
 #define REFRESH_REALLY         0x0001  /* ignore_valid */
 #define REFRESH_UNMERGED       0x0002  /* allow unmerged */
 #define REFRESH_QUIET          0x0004  /* be quiet about it */
@@ -529,6 +533,7 @@ extern int auto_crlf;
 extern int read_replace_refs;
 extern int fsync_object_files;
 extern int core_preload_index;
+extern int core_apply_sparse_checkout;
 
 enum safe_crlf {
        SAFE_CRLF_FALSE = 0,
@@ -618,7 +623,6 @@ static inline void hashclr(unsigned char *hash)
 {
        memset(hash, 0, 20);
 }
-extern int is_empty_blob_sha1(const unsigned char *sha1);
 
 #define EMPTY_TREE_SHA1_HEX \
        "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
@@ -688,7 +692,6 @@ extern int has_sha1_pack(const unsigned char *sha1);
 extern int has_sha1_file(const unsigned char *sha1);
 extern int has_loose_object_nonlocal(const unsigned char *sha1);
 
-extern int has_pack_file(const unsigned char *sha1);
 extern int has_pack_index(const unsigned char *sha1);
 
 extern const signed char hexval_table[256];
@@ -702,7 +705,11 @@ static inline unsigned int hexval(unsigned char c)
 #define DEFAULT_ABBREV 7
 
 extern int get_sha1(const char *str, unsigned char *sha1);
-extern int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode);
+extern int get_sha1_with_mode_1(const char *str, unsigned char *sha1, unsigned *mode, int gently, const char *prefix);
+static inline int get_sha1_with_mode(const char *str, unsigned char *sha1, unsigned *mode)
+{
+       return get_sha1_with_mode_1(str, sha1, mode, 1, NULL);
+}
 extern int get_sha1_hex(const char *hex, unsigned char *sha1);
 extern char *sha1_to_hex(const unsigned char *sha1);   /* static buffer result! */
 extern int read_ref(const char *filename, unsigned char *sha1);
@@ -710,6 +717,7 @@ extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *
 extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
 extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
 extern int interpret_branch_name(const char *str, struct strbuf *);
+extern int get_sha1_mb(const char *str, unsigned char *sha1);
 
 extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
 extern const char *ref_rev_parse_rules[];
@@ -784,8 +792,6 @@ 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);
-extern void clear_lstat_cache(void);
 extern void schedule_dir_for_removal(const char *name, int len);
 extern void remove_scheduled_dirs(void);
 
index e5332efcfc9449e0f3af4d6c49be63adfeb138b1..24128d7a2a0007d5aee2543624f0297d77860ea6 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -73,7 +73,6 @@ struct pretty_print_context
        struct reflog_walk_info *reflog_info;
 };
 
-extern int non_ascii(int);
 extern int has_non_ascii(const char *text);
 struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
 extern char *reencode_commit_message(const struct commit *commit,
index 0d73f15fa894cb0ded20f0cdd1f7fec3da1643f1..ab65f77ab99500d99d24a9b7266669f37bb02cb2 100644 (file)
@@ -3,9 +3,7 @@
 #include <conio.h>
 #include "../strbuf.h"
 
-#include <shellapi.h>
-
-static int err_win_to_posix(DWORD winerr)
+int err_win_to_posix(DWORD winerr)
 {
        int error = ENOSYS;
        switch(winerr) {
@@ -142,12 +140,20 @@ int mingw_open (const char *filename, int oflags, ...)
        return fd;
 }
 
-static inline time_t filetime_to_time_t(const FILETIME *ft)
+/*
+ * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
+ * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
+ */
+static inline long long filetime_to_hnsec(const FILETIME *ft)
 {
        long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
-       winTime -= 116444736000000000LL; /* Windows to Unix Epoch conversion */
-       winTime /= 10000000;             /* Nano to seconds resolution */
-       return (time_t)winTime;
+       /* Windows to Unix Epoch conversion */
+       return winTime - 116444736000000000LL;
+}
+
+static inline time_t filetime_to_time_t(const FILETIME *ft)
+{
+       return (time_t)(filetime_to_hnsec(ft) / 10000000);
 }
 
 /* We keep the do_lstat code in a separate function to avoid recursion.
@@ -283,64 +289,37 @@ int mkstemp(char *template)
 
 int gettimeofday(struct timeval *tv, void *tz)
 {
-       SYSTEMTIME st;
-       struct tm tm;
-       GetSystemTime(&st);
-       tm.tm_year = st.wYear-1900;
-       tm.tm_mon = st.wMonth-1;
-       tm.tm_mday = st.wDay;
-       tm.tm_hour = st.wHour;
-       tm.tm_min = st.wMinute;
-       tm.tm_sec = st.wSecond;
-       tv->tv_sec = tm_to_time_t(&tm);
-       if (tv->tv_sec < 0)
-               return -1;
-       tv->tv_usec = st.wMilliseconds*1000;
+       FILETIME ft;
+       long long hnsec;
+
+       GetSystemTimeAsFileTime(&ft);
+       hnsec = filetime_to_hnsec(&ft);
+       tv->tv_sec = hnsec / 10000000;
+       tv->tv_usec = (hnsec % 10000000) / 10;
        return 0;
 }
 
 int pipe(int filedes[2])
 {
-       int fd;
-       HANDLE h[2], parent;
+       HANDLE h[2];
 
-       if (_pipe(filedes, 8192, 0) < 0)
-               return -1;
-
-       parent = GetCurrentProcess();
-
-       if (!DuplicateHandle (parent, (HANDLE)_get_osfhandle(filedes[0]),
-                       parent, &h[0], 0, FALSE, DUPLICATE_SAME_ACCESS)) {
-               close(filedes[0]);
-               close(filedes[1]);
-               return -1;
-       }
-       if (!DuplicateHandle (parent, (HANDLE)_get_osfhandle(filedes[1]),
-                       parent, &h[1], 0, FALSE, DUPLICATE_SAME_ACCESS)) {
-               close(filedes[0]);
-               close(filedes[1]);
-               CloseHandle(h[0]);
+       /* this creates non-inheritable handles */
+       if (!CreatePipe(&h[0], &h[1], NULL, 8192)) {
+               errno = err_win_to_posix(GetLastError());
                return -1;
        }
-       fd = _open_osfhandle((int)h[0], O_NOINHERIT);
-       if (fd < 0) {
-               close(filedes[0]);
-               close(filedes[1]);
+       filedes[0] = _open_osfhandle((int)h[0], O_NOINHERIT);
+       if (filedes[0] < 0) {
                CloseHandle(h[0]);
                CloseHandle(h[1]);
                return -1;
        }
-       close(filedes[0]);
-       filedes[0] = fd;
-       fd = _open_osfhandle((int)h[1], O_NOINHERIT);
-       if (fd < 0) {
+       filedes[1] = _open_osfhandle((int)h[1], O_NOINHERIT);
+       if (filedes[0] < 0) {
                close(filedes[0]);
-               close(filedes[1]);
                CloseHandle(h[1]);
                return -1;
        }
-       close(filedes[1]);
-       filedes[1] = fd;
        return 0;
 }
 
@@ -638,8 +617,8 @@ static int env_compare(const void *a, const void *b)
        return strcasecmp(*ea, *eb);
 }
 
-static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
-                          int prepend_cmd)
+static pid_t mingw_spawnve_fd(const char *cmd, const char **argv, char **env,
+                             int prepend_cmd, int fhin, int fhout, int fherr)
 {
        STARTUPINFO si;
        PROCESS_INFORMATION pi;
@@ -675,9 +654,9 @@ static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
        memset(&si, 0, sizeof(si));
        si.cb = sizeof(si);
        si.dwFlags = STARTF_USESTDHANDLES;
-       si.hStdInput = (HANDLE) _get_osfhandle(0);
-       si.hStdOutput = (HANDLE) _get_osfhandle(1);
-       si.hStdError = (HANDLE) _get_osfhandle(2);
+       si.hStdInput = (HANDLE) _get_osfhandle(fhin);
+       si.hStdOutput = (HANDLE) _get_osfhandle(fhout);
+       si.hStdError = (HANDLE) _get_osfhandle(fherr);
 
        /* concatenate argv, quoting args as we go */
        strbuf_init(&args, 0);
@@ -732,7 +711,14 @@ static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
        return (pid_t)pi.hProcess;
 }
 
-pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env)
+static pid_t mingw_spawnve(const char *cmd, const char **argv, char **env,
+                          int prepend_cmd)
+{
+       return mingw_spawnve_fd(cmd, argv, env, prepend_cmd, 0, 1, 2);
+}
+
+pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
+                    int fhin, int fhout, int fherr)
 {
        pid_t pid;
        char **path = get_path_split();
@@ -754,13 +740,15 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env)
                                pid = -1;
                        }
                        else {
-                               pid = mingw_spawnve(iprog, argv, env, 1);
+                               pid = mingw_spawnve_fd(iprog, argv, env, 1,
+                                                      fhin, fhout, fherr);
                                free(iprog);
                        }
                        argv[0] = argv0;
                }
                else
-                       pid = mingw_spawnve(prog, argv, env, 0);
+                       pid = mingw_spawnve_fd(prog, argv, env, 0,
+                                              fhin, fhout, fherr);
                free(prog);
        }
        free_path_split(path);
@@ -1338,8 +1326,22 @@ static const char *make_backslash_path(const char *path)
 void mingw_open_html(const char *unixpath)
 {
        const char *htmlpath = make_backslash_path(unixpath);
+       typedef HINSTANCE (WINAPI *T)(HWND, const char *,
+                       const char *, const char *, const char *, INT);
+       T ShellExecute;
+       HMODULE shell32;
+
+       shell32 = LoadLibrary("shell32.dll");
+       if (!shell32)
+               die("cannot load shell32.dll");
+       ShellExecute = (T)GetProcAddress(shell32, "ShellExecuteA");
+       if (!ShellExecute)
+               die("cannot run browser");
+
        printf("Launching default browser to display HTML ...\n");
        ShellExecute(NULL, "open", htmlpath, NULL, "\\", 0);
+
+       FreeLibrary(shell32);
 }
 
 int link(const char *oldpath, const char *newpath)
index b3d299f5bc16810867b7768725b8d33ee999f6b5..e254fb4e068c3248a1aac33d70e40b620cd91088 100644 (file)
@@ -209,18 +209,21 @@ int mingw_getpagesize(void);
  * mingw_fstat() instead of fstat() on Windows.
  */
 #define off_t off64_t
-#define stat _stati64
 #define lseek _lseeki64
+#ifndef ALREADY_DECLARED_STAT_FUNCS
+#define stat _stati64
 int mingw_lstat(const char *file_name, struct stat *buf);
 int mingw_fstat(int fd, struct stat *buf);
 #define fstat mingw_fstat
 #define lstat mingw_lstat
 #define _stati64(x,y) mingw_lstat(x,y)
+#endif
 
 int mingw_utime(const char *file_name, const struct utimbuf *times);
 #define utime mingw_utime
 
-pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env);
+pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **env,
+                    int fhin, int fhout, int fherr);
 void mingw_execvp(const char *cmd, char *const *argv);
 #define execvp mingw_execvp
 
@@ -307,3 +310,8 @@ struct mingw_dirent
 #define readdir(x) mingw_readdir(x)
 struct dirent *mingw_readdir(DIR *dir);
 #endif // !NO_MINGW_REPLACE_READDIR
+
+/*
+ * Used by Pthread API implementation for Windows
+ */
+extern int err_win_to_posix(DWORD winerr);
index 9c753a560fcadb44a6e4afb22828584b6fd20db3..023aba0238c844f79f2662a4b885058acbfc91fd 100644 (file)
@@ -21,30 +21,22 @@ static __inline int strcasecmp (const char *s1, const char *s2)
 }
 
 #undef ERROR
-#undef stat
-#undef _stati64
-#include "compat/mingw.h"
-#undef stat
-#define stat _stati64
+
+/* Use mingw_lstat() instead of lstat()/stat() and mingw_fstat() instead
+ * of fstat(). We add the declaration of these functions here, suppressing
+ * the corresponding declarations in mingw.h, so that we can use the
+ * appropriate structure type (and function) names from the msvc headers.
+ */
+#define stat _stat64
+int mingw_lstat(const char *file_name, struct stat *buf);
+int mingw_fstat(int fd, struct stat *buf);
+#define fstat mingw_fstat
+#define lstat mingw_lstat
 #define _stat64(x,y) mingw_lstat(x,y)
+#define ALREADY_DECLARED_STAT_FUNCS
+
+#include "compat/mingw.h"
+
+#undef ALREADY_DECLARED_STAT_FUNCS
 
-/*
-   Even though _stati64 is normally just defined at _stat64
-   on Windows, we specify it here as a proper struct to avoid
-   compiler warnings about macro redefinition due to magic in
-   mingw.h. Struct taken from ReactOS (GNU GPL license).
-*/
-struct _stati64 {
-       _dev_t  st_dev;
-       _ino_t  st_ino;
-       unsigned short st_mode;
-       short   st_nlink;
-       short   st_uid;
-       short   st_gid;
-       _dev_t  st_rdev;
-       __int64 st_size;
-       time_t  st_atime;
-       time_t  st_mtime;
-       time_t  st_ctime;
-};
 #endif
diff --git a/compat/win32/pthread.c b/compat/win32/pthread.c
new file mode 100644 (file)
index 0000000..631c0a4
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
+ *
+ * DISCLAMER: The implementation is Git-specific, it is subset of original
+ * Pthreads API, without lots of other features that Git doesn't use.
+ * Git also makes sure that the passed arguments are valid, so there's
+ * no need for double-checking.
+ */
+
+#include "../../git-compat-util.h"
+#include "pthread.h"
+
+#include <errno.h>
+#include <limits.h>
+
+static unsigned __stdcall win32_start_routine(void *arg)
+{
+       pthread_t *thread = arg;
+       thread->arg = thread->start_routine(thread->arg);
+       return 0;
+}
+
+int pthread_create(pthread_t *thread, const void *unused,
+                  void *(*start_routine)(void*), void *arg)
+{
+       thread->arg = arg;
+       thread->start_routine = start_routine;
+       thread->handle = (HANDLE)
+               _beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL);
+
+       if (!thread->handle)
+               return errno;
+       else
+               return 0;
+}
+
+int win32_pthread_join(pthread_t *thread, void **value_ptr)
+{
+       DWORD result = WaitForSingleObject(thread->handle, INFINITE);
+       switch (result) {
+               case WAIT_OBJECT_0:
+                       if (value_ptr)
+                               *value_ptr = thread->arg;
+                       return 0;
+               case WAIT_ABANDONED:
+                       return EINVAL;
+               default:
+                       return err_win_to_posix(GetLastError());
+       }
+}
+
+int pthread_cond_init(pthread_cond_t *cond, const void *unused)
+{
+       cond->waiters = 0;
+
+       cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
+       if (!cond->sema)
+               die("CreateSemaphore() failed");
+       return 0;
+}
+
+int pthread_cond_destroy(pthread_cond_t *cond)
+{
+       CloseHandle(cond->sema);
+       cond->sema = NULL;
+
+       return 0;
+}
+
+int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
+{
+       InterlockedIncrement(&cond->waiters);
+
+       /*
+        * Unlock external mutex and wait for signal.
+        * NOTE: we've held mutex locked long enough to increment
+        * waiters count above, so there's no problem with
+        * leaving mutex unlocked before we wait on semaphore.
+        */
+       LeaveCriticalSection(mutex);
+
+       /* let's wait - ignore return value */
+       WaitForSingleObject(cond->sema, INFINITE);
+
+       /* we're done waiting, so make sure we decrease waiters count */
+       InterlockedDecrement(&cond->waiters);
+
+       /* lock external mutex again */
+       EnterCriticalSection(mutex);
+
+       return 0;
+}
+
+int pthread_cond_signal(pthread_cond_t *cond)
+{
+       /*
+        * Access to waiters count is atomic; see "Interlocked Variable Access"
+        * http://msdn.microsoft.com/en-us/library/ms684122(VS.85).aspx
+        */
+       int have_waiters = cond->waiters > 0;
+
+       /*
+        * Signal only when there are waiters
+        */
+       if (have_waiters)
+               return ReleaseSemaphore(cond->sema, 1, NULL) ?
+                       0 : err_win_to_posix(GetLastError());
+       else
+               return 0;
+}
diff --git a/compat/win32/pthread.h b/compat/win32/pthread.h
new file mode 100644 (file)
index 0000000..b8e1bcb
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Header used to adapt pthread-based POSIX code to Windows API threads.
+ *
+ * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
+ */
+
+#ifndef PTHREAD_H
+#define PTHREAD_H
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+
+#include <windows.h>
+
+/*
+ * Defines that adapt Windows API threads to pthreads API
+ */
+#define pthread_mutex_t CRITICAL_SECTION
+
+#define pthread_mutex_init(a,b) InitializeCriticalSection((a))
+#define pthread_mutex_destroy(a) DeleteCriticalSection((a))
+#define pthread_mutex_lock EnterCriticalSection
+#define pthread_mutex_unlock LeaveCriticalSection
+
+/*
+ * Implement simple condition variable for Windows threads, based on ACE
+ * implementation.
+ *
+ * See original implementation: http://bit.ly/1vkDjo
+ * ACE homepage: http://www.cse.wustl.edu/~schmidt/ACE.html
+ * See also: http://www.cse.wustl.edu/~schmidt/win32-cv-1.html
+ */
+typedef struct {
+       volatile LONG waiters;
+       HANDLE sema;
+} pthread_cond_t;
+
+extern int pthread_cond_init(pthread_cond_t *cond, const void *unused);
+
+extern int pthread_cond_destroy(pthread_cond_t *cond);
+
+extern int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex);
+
+extern int pthread_cond_signal(pthread_cond_t *cond);
+
+/*
+ * Simple thread creation implementation using pthread API
+ */
+typedef struct {
+       HANDLE handle;
+       void *(*start_routine)(void*);
+       void *arg;
+} pthread_t;
+
+extern int pthread_create(pthread_t *thread, const void *unused,
+                         void *(*start_routine)(void*), void *arg);
+
+/*
+ * To avoid the need of copying a struct, we use small macro wrapper to pass
+ * pointer to win32_pthread_join instead.
+ */
+#define pthread_join(a, b) win32_pthread_join(&(a), (b))
+
+extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
+
+#endif /* PTHREAD_H */
index 37385ce9d338ecbd2556cef9455000784ebab7e5..f4f2c59a34cd9fc70db940871caaf00b94a6ef2e 100644 (file)
--- a/config.c
+++ b/config.c
@@ -518,6 +518,11 @@ static int git_default_core_config(const char *var, const char *value)
                return 0;
        }
 
+       if (!strcmp(var, "core.sparsecheckout")) {
+               core_apply_sparse_checkout = git_config_bool(var, value);
+               return 0;
+       }
+
        /* Add other config variables here and to Documentation/config.txt. */
        return 0;
 }
index fbfa5f25c1b2c75943464757d571d3fa89e209aa..96517204105425ad15049d3bc713e3fbc8d86baa 100755 (executable)
@@ -142,11 +142,9 @@ __git_ps1 ()
                elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
                        if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
                                if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
-                                       git diff --no-ext-diff --ignore-submodules \
-                                               --quiet --exit-code || w="*"
+                                       git diff --no-ext-diff --quiet --exit-code || w="*"
                                        if git rev-parse --quiet --verify HEAD >/dev/null; then
-                                               git diff-index --cached --quiet \
-                                                       --ignore-submodules HEAD -- || i="+"
+                                               git diff-index --cached --quiet HEAD -- || i="+"
                                        else
                                                i="#"
                                        fi
@@ -163,11 +161,8 @@ __git_ps1 ()
                        fi
                fi
 
-               if [ -n "${1-}" ]; then
-                       printf "$1" "$c${b##refs/heads/}$w$i$s$u$r"
-               else
-                       printf " (%s)" "$c${b##refs/heads/}$w$i$s$u$r"
-               fi
+               local f="$w$i$s$u"
+               printf "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r"
        fi
 }
 
@@ -2022,7 +2017,7 @@ _git_svn ()
                init fetch clone rebase dcommit log find-rev
                set-tree commit-diff info create-ignore propget
                proplist show-ignore show-externals branch tag blame
-               migrate
+               migrate mkdirs reset gc
                "
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
@@ -2069,7 +2064,7 @@ _git_svn ()
                        __gitcomp "--stdin $cmt_opts $fc_opts"
                        ;;
                create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\
-               show-externals,--*)
+               show-externals,--*|mkdirs,--*)
                        __gitcomp "--revision="
                        ;;
                log,--*)
@@ -2106,6 +2101,9 @@ _git_svn ()
                                --no-auth-cache --username=
                                "
                        ;;
+               reset,--*)
+                       __gitcomp "--revision= --parent"
+                       ;;
                *)
                        COMPREPLY=()
                        ;;
index 2a6839d81ee970cb799859c7a833f8f8cb780864..854cd94ba55e498a3ff6c26be3dbe5191faa19dc 100755 (executable)
@@ -59,14 +59,14 @@ def getgitenv(user, date):
     elems = re.compile('(.*?)\s+<(.*)>').match(user)
     if elems:
         env += 'export GIT_AUTHOR_NAME="%s" ;' % elems.group(1)
-        env += 'export GIT_COMMITER_NAME="%s" ;' % elems.group(1)
+        env += 'export GIT_COMMITTER_NAME="%s" ;' % elems.group(1)
         env += 'export GIT_AUTHOR_EMAIL="%s" ;' % elems.group(2)
-        env += 'export GIT_COMMITER_EMAIL="%s" ;' % elems.group(2)
+        env += 'export GIT_COMMITTER_EMAIL="%s" ;' % elems.group(2)
     else:
         env += 'export GIT_AUTHOR_NAME="%s" ;' % user
-        env += 'export GIT_COMMITER_NAME="%s" ;' % user
+        env += 'export GIT_COMMITTER_NAME="%s" ;' % user
         env += 'export GIT_AUTHOR_EMAIL= ;'
-        env += 'export GIT_COMMITER_EMAIL= ;'
+        env += 'export GIT_COMMITTER_EMAIL= ;'
 
     env += 'export GIT_AUTHOR_DATE="%s" ;' % date
     env += 'export GIT_COMMITTER_DATE="%s" ;' % date
index 491e7141b4ea29b3cf754cbaf2656a0c3ca8c46c..950b1f9840663e7536256babb05f68de01a4689b 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -249,10 +249,11 @@ static int filter_buffer(int fd, void *data)
        struct child_process child_process;
        struct filter_params *params = (struct filter_params *)data;
        int write_err, status;
-       const char *argv[] = { "sh", "-c", params->cmd, NULL };
+       const char *argv[] = { params->cmd, NULL };
 
        memset(&child_process, 0, sizeof(child_process));
        child_process.argv = argv;
+       child_process.use_shell = 1;
        child_process.in = -1;
        child_process.out = fd;
 
index 5783e2401108adb1fef6943ef80bd78dbc76ecad..918e560b4e1d4b78bd2d3223db61e915c4a3ccbb 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -562,6 +562,24 @@ static int execute(struct sockaddr *addr)
        return -1;
 }
 
+static int addrcmp(const struct sockaddr_storage *s1,
+    const struct sockaddr_storage *s2)
+{
+       if (s1->ss_family != s2->ss_family)
+               return s1->ss_family - s2->ss_family;
+       if (s1->ss_family == AF_INET)
+               return memcmp(&((struct sockaddr_in *)s1)->sin_addr,
+                   &((struct sockaddr_in *)s2)->sin_addr,
+                   sizeof(struct in_addr));
+#ifndef NO_IPV6
+       if (s1->ss_family == AF_INET6)
+               return memcmp(&((struct sockaddr_in6 *)s1)->sin6_addr,
+                   &((struct sockaddr_in6 *)s2)->sin6_addr,
+                   sizeof(struct in6_addr));
+#endif
+       return 0;
+}
+
 static int max_connections = 32;
 
 static unsigned int live_children;
@@ -576,17 +594,12 @@ static void add_child(pid_t pid, struct sockaddr *addr, int addrlen)
 {
        struct child *newborn, **cradle;
 
-       /*
-        * This must be xcalloc() -- we'll compare the whole sockaddr_storage
-        * but individual address may be shorter.
-        */
        newborn = xcalloc(1, sizeof(*newborn));
        live_children++;
        newborn->pid = pid;
        memcpy(&newborn->address, addr, addrlen);
        for (cradle = &firstborn; *cradle; cradle = &(*cradle)->next)
-               if (!memcmp(&(*cradle)->address, &newborn->address,
-                           sizeof(newborn->address)))
+               if (!addrcmp(&(*cradle)->address, &newborn->address))
                        break;
        newborn->next = *cradle;
        *cradle = newborn;
@@ -619,8 +632,7 @@ static void kill_some_child(void)
                return;
 
        for (; (next = blanket->next); blanket = next)
-               if (!memcmp(&blanket->address, &next->address,
-                           sizeof(next->address))) {
+               if (!addrcmp(&blanket->address, &next->address)) {
                        kill(blanket->pid, SIGTERM);
                        break;
                }
diff --git a/date.c b/date.c
index 5d05ef61cfb140f004702a5ed614afa755c50670..45f3684ceee465170a520ecf5f25dcaa0e5ed84e 100644 (file)
--- a/date.c
+++ b/date.c
@@ -9,7 +9,7 @@
 /*
  * This is like mktime, but without normalization of tm_wday and tm_yday.
  */
-time_t tm_to_time_t(const struct tm *tm)
+static time_t tm_to_time_t(const struct tm *tm)
 {
        static const int mdays[] = {
            0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
index 349f612b615fc152e9d272900b1ac5177f4a025c..1c7e652a8048524e73f2f34833647d7ec1610bc9 100644 (file)
@@ -159,7 +159,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
                                continue;
                }
 
-               if (ce_uptodate(ce))
+               if (ce_uptodate(ce) || ce_skip_worktree(ce))
                        continue;
 
                /* If CE_VALID is set, don't look at workdir for file removal */
@@ -323,7 +323,8 @@ static void do_oneway_diff(struct unpack_trees_options *o,
        int match_missing, cached;
 
        /* if the entry is not checked out, don't examine work tree */
-       cached = o->index_only || (idx && (idx->ce_flags & CE_VALID));
+       cached = o->index_only ||
+               (idx && ((idx->ce_flags & CE_VALID) || ce_skip_worktree(idx)));
        /*
         * Backward compatibility wart - "diff-index -m" does
         * not mean "do not ignore merges", but "match_missing".
diff --git a/diff.c b/diff.c
index d14a575e8e2670247bf8c296e28cf0146e3e03bb..5d713145879d88339a3ea6bef207129f3cac3fef 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -1997,7 +1997,7 @@ static int reuse_worktree_file(const char *name, const unsigned char *sha1, int
         * If ce is marked as "assume unchanged", there is no
         * guarantee that work tree matches what we are looking for.
         */
-       if (ce->ce_flags & CE_VALID)
+       if ((ce->ce_flags & CE_VALID) || ce_skip_worktree(ce))
                return 0;
 
        /*
@@ -2294,7 +2294,7 @@ static void run_external_diff(const char *pgm,
        }
        *arg = NULL;
        fflush(NULL);
-       retval = run_command_v_opt(spawn_arg, 0);
+       retval = run_command_v_opt(spawn_arg, RUN_USING_SHELL);
        remove_tempfile();
        if (retval) {
                fprintf(stderr, "external diff died, stopping at %s.\n", name);
@@ -3818,16 +3818,19 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
        *arg = NULL;
 
        memset(&child, 0, sizeof(child));
+       child.use_shell = 1;
        child.argv = argv;
        child.out = -1;
        if (start_command(&child) != 0 ||
            strbuf_read(&buf, child.out, 0) < 0 ||
            finish_command(&child) != 0) {
+               close(child.out);
                strbuf_release(&buf);
                remove_tempfile();
                error("error running textconv command '%s'", pgm);
                return NULL;
        }
+       close(child.out);
        remove_tempfile();
 
        return strbuf_detach(&buf, outsize);
diff --git a/dir.c b/dir.c
index d0999ba055367c31571b251fb34bb46ed6c7051d..3a8d3e67a529c03659dc950d64f4b0d6226f9925 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -200,11 +200,35 @@ void add_exclude(const char *string, const char *base,
        which->excludes[which->nr++] = x;
 }
 
-static int add_excludes_from_file_1(const char *fname,
-                                   const char *base,
-                                   int baselen,
-                                   char **buf_p,
-                                   struct exclude_list *which)
+static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
+{
+       int pos, len;
+       unsigned long sz;
+       enum object_type type;
+       void *data;
+       struct index_state *istate = &the_index;
+
+       len = strlen(path);
+       pos = index_name_pos(istate, path, len);
+       if (pos < 0)
+               return NULL;
+       if (!ce_skip_worktree(istate->cache[pos]))
+               return NULL;
+       data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
+       if (!data || type != OBJ_BLOB) {
+               free(data);
+               return NULL;
+       }
+       *size = xsize_t(sz);
+       return data;
+}
+
+int add_excludes_from_file_to_list(const char *fname,
+                                  const char *base,
+                                  int baselen,
+                                  char **buf_p,
+                                  struct exclude_list *which,
+                                  int check_index)
 {
        struct stat st;
        int fd, i;
@@ -212,27 +236,32 @@ static int add_excludes_from_file_1(const char *fname,
        char *buf, *entry;
 
        fd = open(fname, O_RDONLY);
-       if (fd < 0 || fstat(fd, &st) < 0)
-               goto err;
-       size = xsize_t(st.st_size);
-       if (size == 0) {
-               close(fd);
-               return 0;
+       if (fd < 0 || fstat(fd, &st) < 0) {
+               if (0 <= fd)
+                       close(fd);
+               if (!check_index ||
+                   (buf = read_skip_worktree_file_from_index(fname, &size)) == NULL)
+                       return -1;
        }
-       buf = xmalloc(size+1);
-       if (read_in_full(fd, buf, size) != size)
-       {
-               free(buf);
-               goto err;
+       else {
+               size = xsize_t(st.st_size);
+               if (size == 0) {
+                       close(fd);
+                       return 0;
+               }
+               buf = xmalloc(size);
+               if (read_in_full(fd, buf, size) != size) {
+                       close(fd);
+                       return -1;
+               }
+               close(fd);
        }
-       close(fd);
 
        if (buf_p)
                *buf_p = buf;
-       buf[size++] = '\n';
        entry = buf;
-       for (i = 0; i < size; i++) {
-               if (buf[i] == '\n') {
+       for (i = 0; i <= size; i++) {
+               if (i == size || buf[i] == '\n') {
                        if (entry != buf + i && entry[0] != '#') {
                                buf[i - (i && buf[i-1] == '\r')] = 0;
                                add_exclude(entry, base, baselen, which);
@@ -241,17 +270,12 @@ static int add_excludes_from_file_1(const char *fname,
                }
        }
        return 0;
-
- err:
-       if (0 <= fd)
-               close(fd);
-       return -1;
 }
 
 void add_excludes_from_file(struct dir_struct *dir, const char *fname)
 {
-       if (add_excludes_from_file_1(fname, "", 0, NULL,
-                                    &dir->exclude_list[EXC_FILE]) < 0)
+       if (add_excludes_from_file_to_list(fname, "", 0, NULL,
+                                          &dir->exclude_list[EXC_FILE], 0) < 0)
                die("cannot use %s as an exclude file", fname);
 }
 
@@ -300,9 +324,9 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
                memcpy(dir->basebuf + current, base + current,
                       stk->baselen - current);
                strcpy(dir->basebuf + stk->baselen, dir->exclude_per_dir);
-               add_excludes_from_file_1(dir->basebuf,
-                                        dir->basebuf, stk->baselen,
-                                        &stk->filebuf, el);
+               add_excludes_from_file_to_list(dir->basebuf,
+                                              dir->basebuf, stk->baselen,
+                                              &stk->filebuf, el, 1);
                dir->exclude_stack = stk;
                current = stk->baselen;
        }
@@ -312,9 +336,9 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
 /* Scan the list and let the last match determine the fate.
  * Return 1 for exclude, 0 for include and -1 for undecided.
  */
-static int excluded_1(const char *pathname,
-                     int pathlen, const char *basename, int *dtype,
-                     struct exclude_list *el)
+int excluded_from_list(const char *pathname,
+                      int pathlen, const char *basename, int *dtype,
+                      struct exclude_list *el)
 {
        int i;
 
@@ -325,6 +349,12 @@ static int excluded_1(const char *pathname,
                        int to_exclude = x->to_exclude;
 
                        if (x->flags & EXC_FLAG_MUSTBEDIR) {
+                               if (!dtype) {
+                                       if (!prefixcmp(pathname, exclude))
+                                               return to_exclude;
+                                       else
+                                               continue;
+                               }
                                if (*dtype == DT_UNKNOWN)
                                        *dtype = get_dtype(NULL, pathname, pathlen);
                                if (*dtype != DT_DIR)
@@ -382,8 +412,8 @@ int excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
 
        prep_exclude(dir, pathname, basename-pathname);
        for (st = EXC_CMDL; st <= EXC_FILE; st++) {
-               switch (excluded_1(pathname, pathlen, basename,
-                                  dtype_p, &dir->exclude_list[st])) {
+               switch (excluded_from_list(pathname, pathlen, basename,
+                                          dtype_p, &dir->exclude_list[st])) {
                case 0:
                        return 0;
                case 1:
diff --git a/dir.h b/dir.h
index 320b6a2f38b9289f910141148b5dddb4cb80815f..3bead5f0e25a864c34f515b7bff4c89b55ce84fe 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -69,7 +69,11 @@ extern int match_pathspec(const char **pathspec, const char *name, int namelen,
 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_from_list(const char *pathname, int pathlen, const char *basename,
+                             int *dtype, struct exclude_list *el);
 extern int excluded(struct dir_struct *, const char *, int *);
+extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
+                                         char **buf_p, struct exclude_list *which, int check_index);
 extern void add_excludes_from_file(struct dir_struct *, const char *fname);
 extern void add_exclude(const char *string, const char *base,
                        int baselen, struct exclude_list *which);
index 615f5754d66ba7ea611a4837b1ff802f9f9de598..d8340031d24828483697c10257806efedfe041f5 100644 (file)
--- a/editor.c
+++ b/editor.c
@@ -36,26 +36,9 @@ int launch_editor(const char *path, struct strbuf *buffer, const char *const *en
                return error("Terminal is dumb, but EDITOR unset");
 
        if (strcmp(editor, ":")) {
-               size_t len = strlen(editor);
-               int i = 0;
-               int failed;
-               const char *args[6];
-               struct strbuf arg0 = STRBUF_INIT;
+               const char *args[] = { editor, path, NULL };
 
-               if (strcspn(editor, "|&;<>()$`\\\"' \t\n*?[#~=%") != len) {
-                       /* there are specials */
-                       strbuf_addf(&arg0, "%s \"$@\"", editor);
-                       args[i++] = "sh";
-                       args[i++] = "-c";
-                       args[i++] = arg0.buf;
-               }
-               args[i++] = editor;
-               args[i++] = path;
-               args[i] = NULL;
-
-               failed = run_command_v_opt_cd_env(args, 0, NULL, env);
-               strbuf_release(&arg0);
-               if (failed)
+               if (run_command_v_opt_cd_env(args, RUN_USING_SHELL, NULL, env))
                        return error("There was a problem with the editor '%s'.",
                                        editor);
        }
diff --git a/entry.c b/entry.c
index 06d24f14c6ba9401637aebfb11659ae747796c06..004182c99d27a6a5825d2429f9104fc7a8f1dc80 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -179,7 +179,7 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
  * This is like 'lstat()', except it refuses to follow symlinks
  * in the path, after skipping "skiplen".
  */
-int check_path(const char *path, int len, struct stat *st, int skiplen)
+static int check_path(const char *path, int len, struct stat *st, int skiplen)
 {
        const char *slash = path + len;
 
@@ -206,7 +206,7 @@ int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *t
        len += ce_namelen(ce);
 
        if (!check_path(path, len, &st, state->base_dir_len)) {
-               unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID);
+               unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
                if (!changed)
                        return 0;
                if (!state->force) {
index 5171d9f9a4a30054983638e9e19e61f422724373..739ec2704031f0dd9d4a380e07f001c09742a7be 100644 (file)
@@ -51,6 +51,7 @@ enum push_default_type push_default = PUSH_DEFAULT_MATCHING;
 enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE;
 char *notes_ref_name;
 int grafts_replace_parents = 1;
+int core_apply_sparse_checkout;
 
 /* Parallel index stat data preload? */
 int core_preload_index = 0;
index dd3c99d60d0ccf2506b08632c35c5ffc2bbc3255..25c3588385af63fdb9782ece39a1a4533b805260 100644 (file)
@@ -19,8 +19,8 @@ Format of STDIN stream:
 
   new_commit ::= 'commit' sp ref_str lf
     mark?
-    ('author' sp name sp '<' email '>' sp when lf)?
-    'committer' sp name sp '<' email '>' sp when lf
+    ('author' (sp name)? sp '<' email '>' sp when lf)?
+    'committer' (sp name)? sp '<' email '>' sp when lf
     commit_msg
     ('from' sp committish lf)?
     ('merge' sp committish lf)*
@@ -47,7 +47,7 @@ Format of STDIN stream:
 
   new_tag ::= 'tag' sp tag_str lf
     'from' sp committish lf
-    ('tagger' sp name sp '<' email '>' sp when lf)?
+    ('tagger' (sp name)? sp '<' email '>' sp when lf)?
     tag_msg;
   tag_msg ::= data;
 
@@ -295,6 +295,9 @@ static unsigned long branch_count;
 static unsigned long branch_load_count;
 static int failure;
 static FILE *pack_edges;
+static unsigned int show_stats = 1;
+static int global_argc;
+static const char **global_argv;
 
 /* Memory pools */
 static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
@@ -317,7 +320,10 @@ static unsigned int object_entry_alloc = 5000;
 static struct object_entry_pool *blocks;
 static struct object_entry *object_table[1 << 16];
 static struct mark_set *marks;
-static const char *mark_file;
+static const char *export_marks_file;
+static const char *import_marks_file;
+static int import_marks_file_from_stream;
+static int relative_marks_paths;
 
 /* Our last blob */
 static struct last_object last_blob = { STRBUF_INIT, 0, 0, 0 };
@@ -351,6 +357,9 @@ static struct recent_command *rc_free;
 static unsigned int cmd_save = 100;
 static uintmax_t next_mark;
 static struct strbuf new_data = STRBUF_INIT;
+static int seen_data_command;
+
+static void parse_argv(void);
 
 static void write_branch_report(FILE *rpt, struct branch *b)
 {
@@ -454,8 +463,8 @@ static void write_crash_report(const char *err)
        fputc('\n', rpt);
        fputs("Marks\n", rpt);
        fputs("-----\n", rpt);
-       if (mark_file)
-               fprintf(rpt, "  exported to %s\n", mark_file);
+       if (export_marks_file)
+               fprintf(rpt, "  exported to %s\n", export_marks_file);
        else
                dump_marks_helper(rpt, 0, marks);
 
@@ -1602,13 +1611,13 @@ static void dump_marks(void)
        int mark_fd;
        FILE *f;
 
-       if (!mark_file)
+       if (!export_marks_file)
                return;
 
-       mark_fd = hold_lock_file_for_update(&mark_lock, mark_file, 0);
+       mark_fd = hold_lock_file_for_update(&mark_lock, export_marks_file, 0);
        if (mark_fd < 0) {
                failure |= error("Unable to write marks file %s: %s",
-                       mark_file, strerror(errno));
+                       export_marks_file, strerror(errno));
                return;
        }
 
@@ -1617,7 +1626,7 @@ static void dump_marks(void)
                int saved_errno = errno;
                rollback_lock_file(&mark_lock);
                failure |= error("Unable to write marks file %s: %s",
-                       mark_file, strerror(saved_errno));
+                       export_marks_file, strerror(saved_errno));
                return;
        }
 
@@ -1633,7 +1642,7 @@ static void dump_marks(void)
                int saved_errno = errno;
                rollback_lock_file(&mark_lock);
                failure |= error("Unable to write marks file %s: %s",
-                       mark_file, strerror(saved_errno));
+                       export_marks_file, strerror(saved_errno));
                return;
        }
 
@@ -1641,11 +1650,47 @@ static void dump_marks(void)
                int saved_errno = errno;
                rollback_lock_file(&mark_lock);
                failure |= error("Unable to commit marks file %s: %s",
-                       mark_file, strerror(saved_errno));
+                       export_marks_file, strerror(saved_errno));
                return;
        }
 }
 
+static void read_marks(void)
+{
+       char line[512];
+       FILE *f = fopen(import_marks_file, "r");
+       if (!f)
+               die_errno("cannot read '%s'", import_marks_file);
+       while (fgets(line, sizeof(line), f)) {
+               uintmax_t mark;
+               char *end;
+               unsigned char sha1[20];
+               struct object_entry *e;
+
+               end = strchr(line, '\n');
+               if (line[0] != ':' || !end)
+                       die("corrupt mark line: %s", line);
+               *end = 0;
+               mark = strtoumax(line + 1, &end, 10);
+               if (!mark || end == line + 1
+                       || *end != ' ' || get_sha1(end + 1, sha1))
+                       die("corrupt mark line: %s", line);
+               e = find_object(sha1);
+               if (!e) {
+                       enum object_type type = sha1_object_info(sha1, NULL);
+                       if (type < 0)
+                               die("object not found: %s", sha1_to_hex(sha1));
+                       e = insert_object(sha1);
+                       e->type = type;
+                       e->pack_id = MAX_PACK_ID;
+                       e->offset = 1; /* just not zero! */
+               }
+               insert_mark(mark, e);
+       }
+       fclose(f);
+}
+
+
 static int read_next_command(void)
 {
        static int stdin_eof = 0;
@@ -1666,6 +1711,12 @@ static int read_next_command(void)
                        if (stdin_eof)
                                return EOF;
 
+                       if (!seen_data_command
+                               && prefixcmp(command_buf.buf, "feature ")
+                               && prefixcmp(command_buf.buf, "option ")) {
+                               parse_argv();
+                       }
+
                        rc = rc_free;
                        if (rc)
                                rc_free = rc->next;
@@ -2305,6 +2356,7 @@ static void parse_new_tag(void)
        struct tag *t;
        uintmax_t from_mark = 0;
        unsigned char sha1[20];
+       enum object_type type;
 
        /* Obtain the new tag name from the rest of our command */
        sp = strchr(command_buf.buf, ' ') + 1;
@@ -2325,19 +2377,18 @@ static void parse_new_tag(void)
        s = lookup_branch(from);
        if (s) {
                hashcpy(sha1, s->sha1);
+               type = OBJ_COMMIT;
        } else if (*from == ':') {
                struct object_entry *oe;
                from_mark = strtoumax(from + 1, NULL, 10);
                oe = find_mark(from_mark);
-               if (oe->type != OBJ_COMMIT)
-                       die("Mark :%" PRIuMAX " not a commit", from_mark);
+               type = oe->type;
                hashcpy(sha1, oe->sha1);
        } else if (!get_sha1(from, sha1)) {
                unsigned long size;
                char *buf;
 
-               buf = read_object_with_reference(sha1,
-                       commit_type, &size, sha1);
+               buf = read_sha1_file(sha1, &type, &size);
                if (!buf || size < 46)
                        die("Not a valid commit: %s", from);
                free(buf);
@@ -2362,7 +2413,7 @@ static void parse_new_tag(void)
                    "object %s\n"
                    "type %s\n"
                    "tag %s\n",
-                   sha1_to_hex(sha1), commit_type, t->name);
+                   sha1_to_hex(sha1), typename(type), t->name);
        if (tagger)
                strbuf_addf(&new_data,
                            "tagger %s\n", tagger);
@@ -2420,39 +2471,140 @@ static void parse_progress(void)
        skip_optional_lf();
 }
 
-static void import_marks(const char *input_file)
+static char* make_fast_import_path(const char *path)
 {
-       char line[512];
-       FILE *f = fopen(input_file, "r");
-       if (!f)
-               die_errno("cannot read '%s'", input_file);
-       while (fgets(line, sizeof(line), f)) {
-               uintmax_t mark;
-               char *end;
-               unsigned char sha1[20];
-               struct object_entry *e;
+       struct strbuf abs_path = STRBUF_INIT;
 
-               end = strchr(line, '\n');
-               if (line[0] != ':' || !end)
-                       die("corrupt mark line: %s", line);
-               *end = 0;
-               mark = strtoumax(line + 1, &end, 10);
-               if (!mark || end == line + 1
-                       || *end != ' ' || get_sha1(end + 1, sha1))
-                       die("corrupt mark line: %s", line);
-               e = find_object(sha1);
-               if (!e) {
-                       enum object_type type = sha1_object_info(sha1, NULL);
-                       if (type < 0)
-                               die("object not found: %s", sha1_to_hex(sha1));
-                       e = insert_object(sha1);
-                       e->type = type;
-                       e->pack_id = MAX_PACK_ID;
-                       e->offset = 1; /* just not zero! */
-               }
-               insert_mark(mark, e);
+       if (!relative_marks_paths || is_absolute_path(path))
+               return xstrdup(path);
+       strbuf_addf(&abs_path, "%s/info/fast-import/%s", get_git_dir(), path);
+       return strbuf_detach(&abs_path, NULL);
+}
+
+static void option_import_marks(const char *marks, int from_stream)
+{
+       if (import_marks_file) {
+               if (from_stream)
+                       die("Only one import-marks command allowed per stream");
+
+               /* read previous mark file */
+               if(!import_marks_file_from_stream)
+                       read_marks();
        }
-       fclose(f);
+
+       import_marks_file = make_fast_import_path(marks);
+       import_marks_file_from_stream = from_stream;
+}
+
+static void option_date_format(const char *fmt)
+{
+       if (!strcmp(fmt, "raw"))
+               whenspec = WHENSPEC_RAW;
+       else if (!strcmp(fmt, "rfc2822"))
+               whenspec = WHENSPEC_RFC2822;
+       else if (!strcmp(fmt, "now"))
+               whenspec = WHENSPEC_NOW;
+       else
+               die("unknown --date-format argument %s", fmt);
+}
+
+static void option_max_pack_size(const char *packsize)
+{
+       max_packsize = strtoumax(packsize, NULL, 0) * 1024 * 1024;
+}
+
+static void option_depth(const char *depth)
+{
+       max_depth = strtoul(depth, NULL, 0);
+       if (max_depth > MAX_DEPTH)
+               die("--depth cannot exceed %u", MAX_DEPTH);
+}
+
+static void option_active_branches(const char *branches)
+{
+       max_active_branches = strtoul(branches, NULL, 0);
+}
+
+static void option_export_marks(const char *marks)
+{
+       export_marks_file = make_fast_import_path(marks);
+}
+
+static void option_export_pack_edges(const char *edges)
+{
+       if (pack_edges)
+               fclose(pack_edges);
+       pack_edges = fopen(edges, "a");
+       if (!pack_edges)
+               die_errno("Cannot open '%s'", edges);
+}
+
+static int parse_one_option(const char *option)
+{
+       if (!prefixcmp(option, "max-pack-size=")) {
+               option_max_pack_size(option + 14);
+       } else if (!prefixcmp(option, "depth=")) {
+               option_depth(option + 6);
+       } else if (!prefixcmp(option, "active-branches=")) {
+               option_active_branches(option + 16);
+       } else if (!prefixcmp(option, "export-pack-edges=")) {
+               option_export_pack_edges(option + 18);
+       } else if (!prefixcmp(option, "quiet")) {
+               show_stats = 0;
+       } else if (!prefixcmp(option, "stats")) {
+               show_stats = 1;
+       } else {
+               return 0;
+       }
+
+       return 1;
+}
+
+static int parse_one_feature(const char *feature, int from_stream)
+{
+       if (!prefixcmp(feature, "date-format=")) {
+               option_date_format(feature + 12);
+       } else if (!prefixcmp(feature, "import-marks=")) {
+               option_import_marks(feature + 13, from_stream);
+       } else if (!prefixcmp(feature, "export-marks=")) {
+               option_export_marks(feature + 13);
+       } else if (!prefixcmp(feature, "relative-marks")) {
+               relative_marks_paths = 1;
+       } else if (!prefixcmp(feature, "no-relative-marks")) {
+               relative_marks_paths = 0;
+       } else if (!prefixcmp(feature, "force")) {
+               force_update = 1;
+       } else {
+               return 0;
+       }
+
+       return 1;
+}
+
+static void parse_feature(void)
+{
+       char *feature = command_buf.buf + 8;
+
+       if (seen_data_command)
+               die("Got feature command '%s' after data command", feature);
+
+       if (parse_one_feature(feature, 1))
+               return;
+
+       die("This version of fast-import does not support feature %s.", feature);
+}
+
+static void parse_option(void)
+{
+       char *option = command_buf.buf + 11;
+
+       if (seen_data_command)
+               die("Got option command '%s' after data command", option);
+
+       if (parse_one_option(option))
+               return;
+
+       die("This version of fast-import does not support option: %s", option);
 }
 
 static int git_pack_config(const char *k, const char *v, void *cb)
@@ -2479,9 +2631,35 @@ static int git_pack_config(const char *k, const char *v, void *cb)
 static const char fast_import_usage[] =
 "git fast-import [--date-format=f] [--max-pack-size=n] [--depth=n] [--active-branches=n] [--export-marks=marks.file]";
 
+static void parse_argv(void)
+{
+       unsigned int i;
+
+       for (i = 1; i < global_argc; i++) {
+               const char *a = global_argv[i];
+
+               if (*a != '-' || !strcmp(a, "--"))
+                       break;
+
+               if (parse_one_option(a + 2))
+                       continue;
+
+               if (parse_one_feature(a + 2, 0))
+                       continue;
+
+               die("unknown option %s", a);
+       }
+       if (i != global_argc)
+               usage(fast_import_usage);
+
+       seen_data_command = 1;
+       if (import_marks_file)
+               read_marks();
+}
+
 int main(int argc, const char **argv)
 {
-       unsigned int i, show_stats = 1;
+       unsigned int i;
 
        git_extract_argv0_path(argv[0]);
 
@@ -2500,52 +2678,8 @@ int main(int argc, const char **argv)
        avail_tree_table = xcalloc(avail_tree_table_sz, sizeof(struct avail_tree_content*));
        marks = pool_calloc(1, sizeof(struct mark_set));
 
-       for (i = 1; i < argc; i++) {
-               const char *a = argv[i];
-
-               if (*a != '-' || !strcmp(a, "--"))
-                       break;
-               else if (!prefixcmp(a, "--date-format=")) {
-                       const char *fmt = a + 14;
-                       if (!strcmp(fmt, "raw"))
-                               whenspec = WHENSPEC_RAW;
-                       else if (!strcmp(fmt, "rfc2822"))
-                               whenspec = WHENSPEC_RFC2822;
-                       else if (!strcmp(fmt, "now"))
-                               whenspec = WHENSPEC_NOW;
-                       else
-                               die("unknown --date-format argument %s", fmt);
-               }
-               else if (!prefixcmp(a, "--max-pack-size="))
-                       max_packsize = strtoumax(a + 16, NULL, 0) * 1024 * 1024;
-               else if (!prefixcmp(a, "--depth=")) {
-                       max_depth = strtoul(a + 8, NULL, 0);
-                       if (max_depth > MAX_DEPTH)
-                               die("--depth cannot exceed %u", MAX_DEPTH);
-               }
-               else if (!prefixcmp(a, "--active-branches="))
-                       max_active_branches = strtoul(a + 18, NULL, 0);
-               else if (!prefixcmp(a, "--import-marks="))
-                       import_marks(a + 15);
-               else if (!prefixcmp(a, "--export-marks="))
-                       mark_file = a + 15;
-               else if (!prefixcmp(a, "--export-pack-edges=")) {
-                       if (pack_edges)
-                               fclose(pack_edges);
-                       pack_edges = fopen(a + 20, "a");
-                       if (!pack_edges)
-                               die_errno("Cannot open '%s'", a + 20);
-               } else if (!strcmp(a, "--force"))
-                       force_update = 1;
-               else if (!strcmp(a, "--quiet"))
-                       show_stats = 0;
-               else if (!strcmp(a, "--stats"))
-                       show_stats = 1;
-               else
-                       die("unknown option %s", a);
-       }
-       if (i != argc)
-               usage(fast_import_usage);
+       global_argc = argc;
+       global_argv = argv;
 
        rc_free = pool_alloc(cmd_save * sizeof(*rc_free));
        for (i = 0; i < (cmd_save - 1); i++)
@@ -2568,9 +2702,20 @@ int main(int argc, const char **argv)
                        parse_checkpoint();
                else if (!prefixcmp(command_buf.buf, "progress "))
                        parse_progress();
+               else if (!prefixcmp(command_buf.buf, "feature "))
+                       parse_feature();
+               else if (!prefixcmp(command_buf.buf, "option git "))
+                       parse_option();
+               else if (!prefixcmp(command_buf.buf, "option "))
+                       /* ignore non-git options*/;
                else
                        die("Unsupported command: %s", command_buf.buf);
        }
+
+       /* argv hasn't been parsed yet, do so */
+       if (!seen_data_command)
+               parse_argv();
+
        end_packfile();
 
        dump_branches();
index 4838cdb9ede8765b37ab663aa59fb1b011a30b4c..2f46fda47bc1aa0a345c5cb7293724497965d75e 100755 (executable)
--- a/git-am.sh
+++ b/git-am.sh
@@ -30,6 +30,7 @@ skip            skip the current patch
 abort           restore the original branch and abort the patching operation.
 committer-date-is-author-date    lie about committer date
 ignore-date     use current timestamp for author date
+rerere-autoupdate update the index with reused conflict resolution if possible
 rebasing*       (internal use for git-rebase)"
 
 . git-sh-setup
@@ -135,7 +136,7 @@ It does not apply to blobs recorded in its index."
            export GIT_MERGE_VERBOSITY=0
     fi
     git-merge-recursive $orig_tree -- HEAD $his_tree || {
-           git rerere
+           git rerere $allow_rerere_autoupdate
            echo Failed to merge in the changes.
            exit 1
     }
@@ -293,6 +294,7 @@ resolvemsg= resume= scissors= no_inbody_headers=
 git_apply_opt=
 committer_date_is_author_date=
 ignore_date=
+allow_rerere_autoupdate=
 
 while test $# != 0
 do
@@ -340,6 +342,8 @@ do
                committer_date_is_author_date=t ;;
        --ignore-date)
                ignore_date=t ;;
+       --rerere-autoupdate|--no-rerere-autoupdate)
+               allow_rerere_autoupdate="$1" ;;
        -q|--quiet)
                GIT_QUIET=t ;;
        --)
index 5c596875c2e78a92031915948444971126d91a78..60c8432f854f345b77b469d64dd1429cee74d30d 100644 (file)
@@ -96,6 +96,7 @@
 #include <sys/poll.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
+#include <termios.h>
 #ifndef NO_SYS_SELECT_H
 #include <sys/select.h>
 #endif
@@ -198,7 +199,6 @@ extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)))
 extern void set_die_routine(NORETURN_PTR void (*routine)(const char *err, va_list params));
 
 extern int prefixcmp(const char *str, const char *prefix);
-extern time_t tm_to_time_t(const struct tm *tm);
 
 static inline const char *skip_prefix(const char *str, const char *prefix)
 {
index 6dc45f5d45a44c9bbe31987c1a10e4554f037d6c..28041060c8a36ac2b89ee179501c2ec726f4606b 100755 (executable)
@@ -104,6 +104,7 @@ $log->info("--------------- STARTING -----------------");
 my $usage =
     "Usage: git cvsserver [options] [pserver|server] [<directory> ...]\n".
     "    --base-path <path>  : Prepend to requested CVSROOT\n".
+    "                          Can be read from GIT_CVSSERVER_BASE_PATH\n".
     "    --strict-paths      : Don't allow recursing into subdirectories\n".
     "    --export-all        : Don't check for gitcvs.enabled in config\n".
     "    --version, -V       : Print version information and exit\n".
@@ -111,7 +112,8 @@ my $usage =
     "\n".
     "<directory> ... is a list of allowed directories. If no directories\n".
     "are given, all are allowed. This is an additional restriction, gitcvs\n".
-    "access still needs to be enabled by the gitcvs.enabled config option.\n";
+    "access still needs to be enabled by the gitcvs.enabled config option.\n".
+    "Alternately, one directory may be specified in GIT_CVSSERVER_ROOT.\n";
 
 my @opts = ( 'help|h|H', 'version|V',
             'base-path=s', 'strict-paths', 'export-all' );
@@ -148,6 +150,24 @@ if ($state->{'export-all'} && !@{$state->{allowed_roots}}) {
     die "--export-all can only be used together with an explicit whitelist\n";
 }
 
+# Environment handling for running under git-shell
+if (exists $ENV{GIT_CVSSERVER_BASE_PATH}) {
+    if ($state->{'base-path'}) {
+       die "Cannot specify base path both ways.\n";
+    }
+    my $base_path = $ENV{GIT_CVSSERVER_BASE_PATH};
+    $state->{'base-path'} = $base_path;
+    $log->debug("Picked up base path '$base_path' from environment.\n");
+}
+if (exists $ENV{GIT_CVSSERVER_ROOT}) {
+    if (@{$state->{allowed_roots}}) {
+       die "Cannot specify roots both ways: @ARGV\n";
+    }
+    my $allowed_root = $ENV{GIT_CVSSERVER_ROOT};
+    $state->{allowed_roots} = [ $allowed_root ];
+    $log->debug("Picked up allowed root '$allowed_root' from environment.\n");
+}
+
 # if we are called with a pserver argument,
 # deal with the authentication cat before entering the
 # main loop
@@ -981,6 +1001,8 @@ sub req_update
 
     #$log->debug("update state : " . Dumper($state));
 
+    my $last_dirname = "///";
+
     # foreach file specified on the command line ...
     foreach my $filename ( @{$state->{args}} )
     {
@@ -988,6 +1010,20 @@ sub req_update
 
         $log->debug("Processing file $filename");
 
+        unless ( $state->{globaloptions}{-Q} || $state->{globaloptions}{-q} )
+        {
+            my $cur_dirname = dirname($filename);
+            if ( $cur_dirname ne $last_dirname )
+            {
+                $last_dirname = $cur_dirname;
+                if ( $cur_dirname eq "" )
+                {
+                    $cur_dirname = ".";
+                }
+                print "E cvs update: Updating $cur_dirname\n";
+            }
+        }
+
         # if we have a -C we should pretend we never saw modified stuff
         if ( exists ( $state->{opt}{C} ) )
         {
index cb9d2022cc93f1f371264098828a52e0d6bf1d7c..195b5ef48ed3371b2010f9caf711e12089da9c06 100755 (executable)
@@ -259,7 +259,6 @@ test -s "$tempdir"/heads ||
 
 GIT_INDEX_FILE="$(pwd)/../index"
 export GIT_INDEX_FILE
-git read-tree || die "Could not seed the index"
 
 # map old->new commit ids for rewriting parents
 mkdir ../map || die "Could not create map/ directory"
index 825c52c2436d15edb12a2ce7850437e0e531e0e3..615753c83c38f4a97752d5ca3400beeef4acd5f1 100755 (executable)
@@ -44,9 +44,8 @@ esac
 # MRC is the current "merge reference commit"
 # MRT is the current "merge result tree"
 
-MRC=$head MSG= PARENT="-p $head"
+MRC=$(git rev-parse --verify -q $head)
 MRT=$(git write-tree)
-CNT=1 ;# counting our head
 NON_FF_MERGE=0
 OCTOPUS_FAILURE=0
 for SHA1 in $remotes
@@ -61,19 +60,17 @@ do
                exit 2
        esac
 
+       eval pretty_name=\${GITHEAD_$SHA1:-$SHA1}
        common=$(git merge-base --all $SHA1 $MRC) ||
-               die "Unable to find common commit with $SHA1"
+               die "Unable to find common commit with $pretty_name"
 
        case "$LF$common$LF" in
        *"$LF$SHA1$LF"*)
-               echo "Already up-to-date with $SHA1"
+               echo "Already up-to-date with $pretty_name"
                continue
                ;;
        esac
 
-       CNT=`expr $CNT + 1`
-       PARENT="$PARENT -p $SHA1"
-
        if test "$common,$NON_FF_MERGE" = "$MRC,0"
        then
                # The first head being merged was a fast-forward.
@@ -81,7 +78,7 @@ do
                # tree as the intermediate result of the merge.
                # We still need to count this as part of the parent set.
 
-               echo "Fast-forwarding to: $SHA1"
+               echo "Fast-forwarding to: $pretty_name"
                git read-tree -u -m $head $SHA1 || exit
                MRC=$SHA1 MRT=$(git write-tree)
                continue
@@ -89,7 +86,7 @@ do
 
        NON_FF_MERGE=1
 
-       echo "Trying simple merge with $SHA1"
+       echo "Trying simple merge with $pretty_name"
        git read-tree -u -m --aggressive  $common $MRT $SHA1 || exit 2
        next=$(git write-tree 2>/dev/null)
        if test $? -ne 0
index d52932878c05af1aeb714c0cf4d48dbbcddc19ee..1560e84bd5d06345dd3976ed55c1cfb8af51b41e 100755 (executable)
@@ -322,7 +322,7 @@ make_squash_message () {
 }
 
 peek_next_command () {
-       sed -n "1s/ .*$//p" < "$TODO"
+       sed -n -e "/^#/d" -e "/^$/d" -e "s/ .*//p" -e "q" < "$TODO"
 }
 
 do_next () {
@@ -495,6 +495,25 @@ get_saved_options () {
        test -f "$DOTEST"/rebase-root && REBASE_ROOT=t
 }
 
+LF='
+'
+parse_onto () {
+       case "$1" in
+       *...*)
+               if      left=${1%...*} right=${1#*...} &&
+                       onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
+               then
+                       case "$onto" in
+                       ?*"$LF"?* | '')
+                               exit 1 ;;
+                       esac
+                       echo "$onto"
+                       exit 0
+               fi
+       esac
+       git rev-parse --verify "$1^0"
+}
+
 while test $# != 0
 do
        case "$1" in
@@ -602,7 +621,7 @@ first and then run 'git rebase --continue' again."
                ;;
        --onto)
                shift
-               ONTO=$(git rev-parse --verify "$1") ||
+               ONTO=$(parse_onto "$1") ||
                        die "Does not point to a valid commit: $1"
                ;;
        --)
index b121f4537ccb173d9f289734f1fe2e89b28d7562..eddc02875f3802844a4c284aaedef48417f4580d 100755 (executable)
@@ -34,6 +34,8 @@ set_reflog_action rebase
 require_work_tree
 cd_to_toplevel
 
+LF='
+'
 OK_TO_SKIP_PRE_REBASE=
 RESOLVEMSG="
 When you have resolved this problem run \"git rebase --continue\".
@@ -50,6 +52,7 @@ diffstat=$(git config --bool rebase.stat)
 git_am_opt=
 rebase_root=
 force_rebase=
+allow_rerere_autoupdate=
 
 continue_merge () {
        test -n "$prev_head" || die "prev_head must be defined"
@@ -118,7 +121,7 @@ call_merge () {
                return
                ;;
        1)
-               git rerere
+               git rerere $allow_rerere_autoupdate
                die "$RESOLVEMSG"
                ;;
        2)
@@ -349,6 +352,9 @@ do
        -f|--f|--fo|--for|--forc|force|--force-r|--force-re|--force-reb|--force-reba|--force-rebas|--force-rebase)
                force_rebase=t
                ;;
+       --rerere-autoupdate|--no-rerere-autoupdate)
+               allow_rerere_autoupdate="$1"
+               ;;
        -*)
                usage
                ;;
@@ -417,7 +423,27 @@ fi
 
 # Make sure the branch to rebase onto is valid.
 onto_name=${newbase-"$upstream_name"}
-onto=$(git rev-parse --verify "${onto_name}^0") || exit
+case "$onto_name" in
+*...*)
+       if      left=${onto_name%...*} right=${onto_name#*...} &&
+               onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
+       then
+               case "$onto" in
+               ?*"$LF"?*)
+                       die "$onto_name: there are more than one merge bases"
+                       ;;
+               '')
+                       die "$onto_name: there is no merge base"
+                       ;;
+               esac
+       else
+               die "$onto_name: there is no merge base"
+       fi
+       ;;
+*)
+       onto=$(git rev-parse --verify "${onto_name}^0") || exit
+       ;;
+esac
 
 # If a hook exists, give it a chance to interrupt
 run_pre_rebase_hook "$upstream_arg" "$@"
index dfcb8078f508d6cc26d312d61b64e0185e90b74b..d56426dd396190a07de7d661cd22fb284b759ca8 100755 (executable)
@@ -120,20 +120,11 @@ is_bare_repository () {
 }
 
 cd_to_toplevel () {
-       cdup=$(git rev-parse --show-cdup)
-       if test ! -z "$cdup"
-       then
-               # The "-P" option says to follow "physical" directory
-               # structure instead of following symbolic links.  When cdup is
-               # "../", this means following the ".." entry in the current
-               # directory instead textually removing a symlink path element
-               # from the PWD shell variable.  The "-P" behavior is more
-               # consistent with the C-style chdir used by most of Git.
-               cd -P "$cdup" || {
-                       echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
-                       exit 1
-               }
-       fi
+       cdup=$(git rev-parse --show-toplevel) &&
+       cd "$cdup" || {
+               echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
+               exit 1
+       }
 }
 
 require_work_tree () {
index f796c2fe24f0eaec3de45bc5bf14f7dd2e29f4bd..3a0685f1893098e8f5c877f509183c8434e7c028 100755 (executable)
@@ -7,7 +7,7 @@ USAGE="list [<options>]
    or: $dashless drop [-q|--quiet] [<stash>]
    or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
    or: $dashless branch <branchname> [<stash>]
-   or: $dashless [save [-k|--keep-index] [-q|--quiet] [<message>]]
+   or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] [<message>]]
    or: $dashless clear"
 
 SUBDIRECTORY_OK=Yes
index f729488fc5f787c0f4997aacdd1bb14732d717f7..345c12b79064f23e0ae0a15781731b9a42272d83 100644 (file)
@@ -648,6 +648,9 @@ int main(int argc, char **argv)
        setup_path();
        if (!enter_repo(dir, 0))
                not_found("Not a git repository: '%s'", dir);
+       if (!getenv("GIT_HTTP_EXPORT_ALL") &&
+           access("git-daemon-export-ok", F_OK) )
+               not_found("Repository not exported: '%s'", dir);
 
        git_config(http_config, NULL);
        cmd->imp(cmd_arg);
diff --git a/http.c b/http.c
index ed6414a2aaa4e0f6cf7672a089f49060aad62bfb..deab59551dad9a0d2c2e86d75071fa561e4cbf1a 100644 (file)
--- a/http.c
+++ b/http.c
@@ -7,6 +7,12 @@ int active_requests;
 int http_is_verbose;
 size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
 
+#if LIBCURL_VERSION_NUM >= 0x070a06
+#define LIBCURL_CAN_HANDLE_AUTH_ANY
+#endif
+
+static int min_curl_sessions = 1;
+static int curl_session_count;
 #ifdef USE_CURL_MULTI
 static int max_requests = -1;
 static CURLM *curlm;
@@ -152,6 +158,14 @@ static int http_options(const char *var, const char *value, void *cb)
                        ssl_cert_password_required = 1;
                return 0;
        }
+       if (!strcmp("http.minsessions", var)) {
+               min_curl_sessions = git_config_int(var, value);
+#ifndef USE_CURL_MULTI
+               if (min_curl_sessions > 1)
+                       min_curl_sessions = 1;
+#endif
+               return 0;
+       }
 #ifdef USE_CURL_MULTI
        if (!strcmp("http.maxrequests", var)) {
                max_requests = git_config_int(var, value);
@@ -230,6 +244,9 @@ static CURL *get_curl_handle(void)
 #if LIBCURL_VERSION_NUM >= 0x070907
        curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
 #endif
+#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
+       curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
+#endif
 
        init_curl_http_auth(result);
 
@@ -372,6 +389,7 @@ void http_init(struct remote *remote)
        if (curl_ssl_verify == -1)
                curl_ssl_verify = 1;
 
+       curl_session_count = 0;
 #ifdef USE_CURL_MULTI
        if (max_requests < 1)
                max_requests = DEFAULT_MAX_REQUESTS;
@@ -480,6 +498,7 @@ struct active_request_slot *get_active_slot(void)
 #else
                slot->curl = curl_easy_duphandle(curl_default);
 #endif
+               curl_session_count++;
        }
 
        active_requests++;
@@ -558,9 +577,11 @@ void fill_active_slots(void)
        }
 
        while (slot != NULL) {
-               if (!slot->in_use && slot->curl != NULL) {
+               if (!slot->in_use && slot->curl != NULL
+                       && curl_session_count > min_curl_sessions) {
                        curl_easy_cleanup(slot->curl);
                        slot->curl = NULL;
+                       curl_session_count--;
                }
                slot = slot->next;
        }
@@ -630,15 +651,16 @@ static void closedown_active_slot(struct active_request_slot *slot)
        slot->in_use = 0;
 }
 
-void release_active_slot(struct active_request_slot *slot)
+static void release_active_slot(struct active_request_slot *slot)
 {
        closedown_active_slot(slot);
-       if (slot->curl) {
+       if (slot->curl && curl_session_count > min_curl_sessions) {
 #ifdef USE_CURL_MULTI
                curl_multi_remove_handle(curlm, slot->curl);
 #endif
                curl_easy_cleanup(slot->curl);
                slot->curl = NULL;
+               curl_session_count--;
        }
 #ifdef USE_CURL_MULTI
        fill_active_slots();
@@ -812,7 +834,13 @@ int http_get_strbuf(const char *url, struct strbuf *result, int options)
        return http_request(url, result, HTTP_REQUEST_STRBUF, options);
 }
 
-int http_get_file(const char *url, const char *filename, int options)
+/*
+ * Downloads an url and stores the result in the given file.
+ *
+ * If a previous interrupted download is detected (i.e. a previous temporary
+ * file is still around) the download is resumed.
+ */
+static int http_get_file(const char *url, const char *filename, int options)
 {
        int ret;
        struct strbuf tmpfile = STRBUF_INIT;
@@ -1244,7 +1272,7 @@ int finish_http_object_request(struct http_object_request *freq)
        process_http_object_request(freq);
 
        if (freq->http_code == 416) {
-               fprintf(stderr, "Warning: requested range invalid; we may already have all the data.\n");
+               warning("requested range invalid; we may already have all the data.");
        } else if (freq->curl_result != CURLE_OK) {
                if (stat(freq->tmpfile, &st) == 0)
                        if (st.st_size == 0)
diff --git a/http.h b/http.h
index f828e1d8067aeff3036c14a1c43e333902e46bd6..5c9441c10ce708be426afe7424d63dcbb68a49e2 100644 (file)
--- a/http.h
+++ b/http.h
@@ -81,7 +81,6 @@ extern int start_active_slot(struct active_request_slot *slot);
 extern void run_active_slot(struct active_request_slot *slot);
 extern void finish_active_slot(struct active_request_slot *slot);
 extern void finish_all_active_slots(void);
-extern void release_active_slot(struct active_request_slot *slot);
 
 #ifdef USE_CURL_MULTI
 extern void fill_active_slots(void);
@@ -135,14 +134,6 @@ extern char *get_remote_object_url(const char *url, const char *hex,
  */
 int http_get_strbuf(const char *url, struct strbuf *result, int options);
 
-/*
- * Downloads an url and stores the result in the given file.
- *
- * If a previous interrupted download is detected (i.e. a previous temporary
- * file is still around) the download is resumed.
- */
-int http_get_file(const char *url, const char *filename, int options);
-
 /*
  * Prints an error message using error() containing url and curl_errorstr,
  * and returns ret.
index de8114bac010ef095cf9de17671566e248366dcb..51f371ba9f08637657ec9198cf3f16d0c0407232 100644 (file)
@@ -965,17 +965,13 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
        /* open connection to IMAP server */
 
        if (srvc->tunnel) {
-               const char *argv[4];
+               const char *argv[] = { srvc->tunnel, NULL };
                struct child_process tunnel = {0};
 
                imap_info("Starting tunnel '%s'... ", srvc->tunnel);
 
-               argv[0] = "sh";
-               argv[1] = "-c";
-               argv[2] = srvc->tunnel;
-               argv[3] = NULL;
-
                tunnel.argv = argv;
+               tunnel.use_shell = 1;
                tunnel.in = -1;
                tunnel.out = -1;
                if (start_command(&tunnel))
index 2d6b6d6cb1d2bc2d334bf058feb3444e94b5a781..18511e281fe7435c2ae93ba3faf502cc606e62b9 100644 (file)
@@ -175,7 +175,7 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
                { "B", temp[2] },
                { NULL }
        };
-       const char *args[] = { "sh", "-c", NULL, NULL };
+       const char *args[] = { NULL, NULL };
        int status, fd, i;
        struct stat st;
 
@@ -190,8 +190,8 @@ static int ll_ext_merge(const struct ll_merge_driver *fn,
 
        strbuf_expand(&cmd, fn->cmdline, strbuf_expand_dict_cb, &dict);
 
-       args[2] = cmd.buf;
-       status = run_command_v_opt(args, 0);
+       args[0] = cmd.buf;
+       status = run_command_v_opt(args, RUN_USING_SHELL);
        fd = open(temp[1], O_RDONLY);
        if (fd < 0)
                goto bad;
index 6851fa55a503bd24dd204934d7433e306996ce09..b0d74cdddee2407de590cb9f725e8e24093d9d35 100644 (file)
@@ -164,9 +164,10 @@ static char *unable_to_lock_message(const char *path, int err)
                    "If no other git process is currently running, this probably means a\n"
                    "git process crashed in this repository earlier. Make sure no other git\n"
                    "process is running and remove the file manually to continue.",
-                   path, strerror(err));
+                           make_nonrelative_path(path), strerror(err));
        } else
-               strbuf_addf(&buf, "Unable to create '%s.lock': %s", path, strerror(err));
+               strbuf_addf(&buf, "Unable to create '%s.lock': %s",
+                           make_nonrelative_path(path), strerror(err));
        return strbuf_detach(&buf, NULL);
 }
 
index f167c005bf9039f44d21ef5f32ab27db7784d642..b68c1fec9c2ca69f313bc3fa16461c6c9150c53d 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -243,8 +243,3 @@ int map_user(struct string_list *map,
        debug_mm("map_user:  --\n");
        return 0;
 }
-
-int map_email(struct string_list *map, const char *email, char *name, int maxlen)
-{
-       return map_user(map, (char *)email, 0, name, maxlen);
-}
index 4b2ca3a7de972c10f214b38a25be522abcbbafd0..d5c3664322023d6552cb24006d833aa08224ff6f 100644 (file)
--- a/mailmap.h
+++ b/mailmap.h
@@ -4,7 +4,6 @@
 int read_mailmap(struct string_list *map, char **repo_abbrev);
 void clear_mailmap(struct string_list *map);
 
-int map_email(struct string_list *mailmap, const char *email, char *name, int maxlen);
 int map_user(struct string_list *mailmap,
             char *email, int maxlen_email, char *name, int maxlen_name);
 
index fe8eaaf19f71b48d9acba83594d918fc4875f5c4..3ca92c4c4def46af10556dbe9b3f48774b9a4a35 100644 (file)
--- a/object.c
+++ b/object.c
@@ -217,27 +217,6 @@ struct object_list *object_list_insert(struct object *item,
         return new_list;
 }
 
-void object_list_append(struct object *item,
-                       struct object_list **list_p)
-{
-       while (*list_p) {
-               list_p = &((*list_p)->next);
-       }
-       *list_p = xmalloc(sizeof(struct object_list));
-       (*list_p)->next = NULL;
-       (*list_p)->item = item;
-}
-
-unsigned object_list_length(struct object_list *list)
-{
-       unsigned ret = 0;
-       while (list) {
-               list = list->next;
-               ret++;
-       }
-       return ret;
-}
-
 int object_list_contains(struct object_list *list, struct object *obj)
 {
        while (list) {
index 89dd0c47a6c86fd3a63370c84e574e799830e1d3..82877c831ca7fe39abbb98805f5801bdc6fcac9c 100644 (file)
--- a/object.h
+++ b/object.h
@@ -72,11 +72,6 @@ struct object *lookup_unknown_object(const unsigned  char *sha1);
 struct object_list *object_list_insert(struct object *item,
                                       struct object_list **list_p);
 
-void object_list_append(struct object *item,
-                       struct object_list **list_p);
-
-unsigned object_list_length(struct object_list *list);
-
 int object_list_contains(struct object_list *list, struct object *obj);
 
 /* Object array handling .. */
diff --git a/pager.c b/pager.c
index 92c03f654abd0333bd0dd48b4aebf9ae42ac4de5..2c7e8ecb3c0860b00113e348008415ca28a7ff72 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -28,7 +28,7 @@ static void pager_preexec(void)
 }
 #endif
 
-static const char *pager_argv[] = { "sh", "-c", NULL, NULL };
+static const char *pager_argv[] = { NULL, NULL };
 static struct child_process pager_process;
 
 static void wait_for_pager(void)
@@ -81,7 +81,8 @@ void setup_pager(void)
        spawned_pager = 1; /* means we are emitting to terminal */
 
        /* spawn the pager */
-       pager_argv[2] = pager;
+       pager_argv[0] = pager;
+       pager_process.use_shell = 1;
        pager_process.argv = pager_argv;
        pager_process.in = -1;
        if (!getenv("LESS")) {
index f5594114ede8a50090c8b97987b52a660235fa56..d218122af5c2c0cddd857fce0ae5064bf32f6387 100644 (file)
@@ -3,6 +3,9 @@
 #include "cache.h"
 #include "commit.h"
 
+static int parse_options_usage(const char * const *usagestr,
+                              const struct option *opts);
+
 #define OPT_SHORT 1
 #define OPT_UNSET 2
 
@@ -560,8 +563,8 @@ void usage_msg_opt(const char *msg,
        usage_with_options(usagestr, options);
 }
 
-int parse_options_usage(const char * const *usagestr,
-                       const struct option *opts)
+static int parse_options_usage(const char * const *usagestr,
+                              const struct option *opts)
 {
        return usage_with_options_internal(usagestr, opts, 0);
 }
@@ -633,3 +636,10 @@ int parse_opt_with_commit(const struct option *opt, const char *arg, int unset)
        commit_list_insert(commit, opt->value);
        return 0;
 }
+
+int parse_opt_tertiary(const struct option *opt, const char *arg, int unset)
+{
+       int *target = opt->value;
+       *target = unset ? 2 : 1;
+       return 0;
+}
index f295a2cf858f4dbebc62e956878544ffdb7fbe58..0c996916b6044989f0e2945881c7c12f7292d5c1 100644 (file)
@@ -123,6 +123,8 @@ struct option {
                                      (h), PARSE_OPT_NOARG, NULL, (p) }
 #define OPT_INTEGER(s, l, v, h)     { OPTION_INTEGER, (s), (l), (v), "n", (h) }
 #define OPT_STRING(s, l, v, a, h)   { OPTION_STRING,  (s), (l), (v), (a), (h) }
+#define OPT_UYN(s, l, v, h)         { OPTION_CALLBACK, (s), (l), (v), NULL, \
+                                     (h), PARSE_OPT_NOARG, &parse_opt_tertiary }
 #define OPT_DATE(s, l, v, h) \
        { OPTION_CALLBACK, (s), (l), (v), "time",(h), 0, \
          parse_opt_approxidate_cb }
@@ -171,9 +173,6 @@ struct parse_opt_ctx_t {
        const char *prefix;
 };
 
-extern int parse_options_usage(const char * const *usagestr,
-                              const struct option *opts);
-
 extern void parse_options_start(struct parse_opt_ctx_t *ctx,
                                int argc, const char **argv, const char *prefix,
                                int flags);
@@ -190,6 +189,7 @@ extern int parse_opt_abbrev_cb(const struct option *, const char *, int);
 extern int parse_opt_approxidate_cb(const struct option *, const char *, int);
 extern int parse_opt_verbosity_cb(const struct option *, const char *, int);
 extern int parse_opt_with_commit(const struct option *, const char *, int);
+extern int parse_opt_tertiary(const struct option *, const char *, int);
 
 #define OPT__VERBOSE(var)  OPT_BOOLEAN('v', "verbose", (var), "be verbose")
 #define OPT__QUIET(var)    OPT_BOOLEAN('q', "quiet",   (var), "be quiet")
index 8f5bd1ab7f119715564fb13cb74de3937d1a3774..9001379a9dd93818e962ae73cdf85dc178804256 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -83,7 +83,7 @@ static int get_one_line(const char *msg)
 }
 
 /* High bit set, or ISO-2022-INT */
-int non_ascii(int ch)
+static int non_ascii(int ch)
 {
        return !isascii(ch) || ch == '\033';
 }
diff --git a/quote.c b/quote.c
index 848d174cc56be32483ba6c73f9b06bc398ed6437..acb6bf929f3e5807139fa7852f739e6ff5a50722 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -72,7 +72,7 @@ void sq_quote_argv(struct strbuf *dst, const char** argv, size_t maxlen)
        }
 }
 
-char *sq_dequote_step(char *arg, char **next)
+static char *sq_dequote_step(char *arg, char **next)
 {
        char *dst = arg;
        char *src = arg;
diff --git a/quote.h b/quote.h
index 66730f2bff3cee42bc7c670e2a6d7da240db1d08..f83eb233c4b153c4c073b3ccd5bf2f5dd926b739 100644 (file)
--- a/quote.h
+++ b/quote.h
@@ -45,7 +45,6 @@ extern char *sq_dequote(char *);
  * next argument that should be passed as first parameter. When there
  * is no more argument to be dequoted, "next" is updated to point to NULL.
  */
-extern char *sq_dequote_step(char *arg, char **next);
 extern int sq_dequote_to_argv(char *arg, const char ***argv, int *nr, int *alloc);
 
 extern int unquote_c_style(struct strbuf *, const char *quoted, const char **endp);
index 9033dd3ab938e2ee7b4248ff97645414ee3688db..f4512967b8393e7934812e667d42a97f4d67ba8c 100644 (file)
@@ -15,6 +15,8 @@
 #include "revision.h"
 #include "blob.h"
 
+static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really);
+
 /* Index extensions.
  *
  * The first letter should be 'A'..'Z' for extensions that are not
@@ -156,7 +158,7 @@ static int ce_modified_check_fs(struct cache_entry *ce, struct stat *st)
        return 0;
 }
 
-int is_empty_blob_sha1(const unsigned char *sha1)
+static int is_empty_blob_sha1(const unsigned char *sha1)
 {
        static const unsigned char empty_blob_sha1[20] = {
                0xe6,0x9d,0xe2,0x9b,0xb2,0xd1,0xd6,0x43,0x4b,0x8b,
@@ -259,12 +261,17 @@ int ie_match_stat(const struct index_state *istate,
 {
        unsigned int changed;
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
+       int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
        int assume_racy_is_modified = options & CE_MATCH_RACY_IS_DIRTY;
 
        /*
         * If it's marked as always valid in the index, it's
         * valid whatever the checked-out copy says.
+        *
+        * skip-worktree has the same effect with higher precedence
         */
+       if (!ignore_skip_worktree && ce_skip_worktree(ce))
+               return 0;
        if (!ignore_valid && (ce->ce_flags & CE_VALID))
                return 0;
 
@@ -564,7 +571,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
        int size, namelen, was_same;
        mode_t st_mode = st->st_mode;
        struct cache_entry *ce, *alias;
-       unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_RACY_IS_DIRTY;
+       unsigned ce_option = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE|CE_MATCH_RACY_IS_DIRTY;
        int verbose = flags & (ADD_CACHE_VERBOSE | ADD_CACHE_PRETEND);
        int pretend = flags & ADD_CACHE_PRETEND;
        int intent_only = flags & ADD_CACHE_INTENT;
@@ -1000,14 +1007,20 @@ static struct cache_entry *refresh_cache_ent(struct index_state *istate,
        struct cache_entry *updated;
        int changed, size;
        int ignore_valid = options & CE_MATCH_IGNORE_VALID;
+       int ignore_skip_worktree = options & CE_MATCH_IGNORE_SKIP_WORKTREE;
 
        if (ce_uptodate(ce))
                return ce;
 
        /*
-        * CE_VALID means the user promised us that the change to
-        * the work tree does not matter and told us not to worry.
+        * CE_VALID or CE_SKIP_WORKTREE means the user promised us
+        * that the change to the work tree does not matter and told
+        * us not to worry.
         */
+       if (!ignore_skip_worktree && ce_skip_worktree(ce)) {
+               ce_mark_uptodate(ce);
+               return ce;
+       }
        if (!ignore_valid && (ce->ce_flags & CE_VALID)) {
                ce_mark_uptodate(ce);
                return ce;
@@ -1141,7 +1154,7 @@ int refresh_index(struct index_state *istate, unsigned int flags, const char **p
        return has_errors;
 }
 
-struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
+static struct cache_entry *refresh_cache_entry(struct cache_entry *ce, int really)
 {
        return refresh_cache_ent(&the_index, ce, really, NULL);
 }
@@ -1606,9 +1619,8 @@ int read_index_unmerged(struct index_state *istate)
                len = strlen(ce->name);
                size = cache_entry_size(len);
                new_ce = xcalloc(1, size);
-               hashcpy(new_ce->sha1, ce->sha1);
                memcpy(new_ce->name, ce->name, len);
-               new_ce->ce_flags = create_ce_flags(len, 0);
+               new_ce->ce_flags = create_ce_flags(len, 0) | CE_CONFLICTED;
                new_ce->ce_mode = ce->ce_mode;
                if (add_index_entry(istate, new_ce, 0))
                        return error("%s: cannot drop to stage #0",
index a331bae6c8e95042dc2d136fdc1ef6b3d4463c53..136100695994049f2e9d6357cb25dd5a9d73ae28 100644 (file)
@@ -290,6 +290,7 @@ struct rpc_state {
        int out;
        struct strbuf result;
        unsigned gzip_request : 1;
+       unsigned initial_buffer : 1;
 };
 
 static size_t rpc_out(void *ptr, size_t eltsize,
@@ -300,6 +301,7 @@ static size_t rpc_out(void *ptr, size_t eltsize,
        size_t avail = rpc->len - rpc->pos;
 
        if (!avail) {
+               rpc->initial_buffer = 0;
                avail = packet_read_line(rpc->out, rpc->buf, rpc->alloc);
                if (!avail)
                        return 0;
@@ -314,6 +316,29 @@ static size_t rpc_out(void *ptr, size_t eltsize,
        return avail;
 }
 
+#ifndef NO_CURL_IOCTL
+static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
+{
+       struct rpc_state *rpc = clientp;
+
+       switch (cmd) {
+       case CURLIOCMD_NOP:
+               return CURLIOE_OK;
+
+       case CURLIOCMD_RESTARTREAD:
+               if (rpc->initial_buffer) {
+                       rpc->pos = 0;
+                       return CURLIOE_OK;
+               }
+               fprintf(stderr, "Unable to rewind rpc post data - try increasing http.postBuffer\n");
+               return CURLIOE_FAILRESTART;
+
+       default:
+               return CURLIOE_UNKNOWNCMD;
+       }
+}
+#endif
+
 static size_t rpc_in(const void *ptr, size_t eltsize,
                size_t nmemb, void *buffer_)
 {
@@ -370,8 +395,13 @@ static int post_rpc(struct rpc_state *rpc)
                 */
                headers = curl_slist_append(headers, "Expect: 100-continue");
                headers = curl_slist_append(headers, "Transfer-Encoding: chunked");
+               rpc->initial_buffer = 1;
                curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, rpc_out);
                curl_easy_setopt(slot->curl, CURLOPT_INFILE, rpc);
+#ifndef NO_CURL_IOCTL
+               curl_easy_setopt(slot->curl, CURLOPT_IOCTLFUNCTION, rpc_ioctl);
+               curl_easy_setopt(slot->curl, CURLOPT_IOCTLDATA, rpc);
+#endif
                if (options.verbosity > 1) {
                        fprintf(stderr, "POST %s (chunked)\n", rpc->service_name);
                        fflush(stderr);
@@ -480,7 +510,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
        strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
        rpc->hdr_content_type = strbuf_detach(&buf, NULL);
 
-       strbuf_addf(&buf, "Accept: application/x-%s-response", svc);
+       strbuf_addf(&buf, "Accept: application/x-%s-result", svc);
        rpc->hdr_accept = strbuf_detach(&buf, NULL);
 
        while (!err) {
index 29f95f657d04300e375d007e9d01832b89311602..e0ac5bcaed7da9cfdff10afc9d1ebb8c27f7ba9f 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -367,7 +367,7 @@ static int is_rerere_enabled(void)
        return 1;
 }
 
-int setup_rerere(struct string_list *merge_rr)
+int setup_rerere(struct string_list *merge_rr, int flags)
 {
        int fd;
 
@@ -375,6 +375,8 @@ int setup_rerere(struct string_list *merge_rr)
        if (!is_rerere_enabled())
                return -1;
 
+       if (flags & (RERERE_AUTOUPDATE|RERERE_NOAUTOUPDATE))
+               rerere_autoupdate = !!(flags & RERERE_AUTOUPDATE);
        merge_rr_path = git_pathdup("MERGE_RR");
        fd = hold_lock_file_for_update(&write_lock, merge_rr_path,
                                       LOCK_DIE_ON_ERROR);
@@ -382,12 +384,12 @@ int setup_rerere(struct string_list *merge_rr)
        return fd;
 }
 
-int rerere(void)
+int rerere(int flags)
 {
        struct string_list merge_rr = { NULL, 0, 0, 1 };
        int fd;
 
-       fd = setup_rerere(&merge_rr);
+       fd = setup_rerere(&merge_rr, flags);
        if (fd < 0)
                return 0;
        return do_plain_rerere(&merge_rr, fd);
index 13313f3f2b2cc6a8d895305b7ac92c12c1753682..10a94a4ea10aa4c7a1ba4ea8397b82f1278afc9d 100644 (file)
--- a/rerere.h
+++ b/rerere.h
@@ -3,9 +3,15 @@
 
 #include "string-list.h"
 
-extern int setup_rerere(struct string_list *);
-extern int rerere(void);
+#define RERERE_AUTOUPDATE   01
+#define RERERE_NOAUTOUPDATE 02
+
+extern int setup_rerere(struct string_list *, int);
+extern int rerere(int);
 extern const char *rerere_path(const char *hex, const char *file);
 extern int has_rerere_resolution(const char *hex);
 
+#define OPT_RERERE_AUTOUPDATE(v) OPT_UYN(0, "rerere-autoupdate", (v), \
+       "update the index with reused conflict resolution if possible")
+
 #endif
index cf2d8f7fae1356e50736cb9d599625df79738a2a..a90984576411d237c1b83b66427e99d9e37ea7c8 100644 (file)
@@ -8,12 +8,58 @@ static inline void close_pair(int fd[2])
        close(fd[1]);
 }
 
+#ifndef WIN32
 static inline void dup_devnull(int to)
 {
        int fd = open("/dev/null", O_RDWR);
        dup2(fd, to);
        close(fd);
 }
+#endif
+
+static const char **prepare_shell_cmd(const char **argv)
+{
+       int argc, nargc = 0;
+       const char **nargv;
+
+       for (argc = 0; argv[argc]; argc++)
+               ; /* just counting */
+       /* +1 for NULL, +3 for "sh -c" plus extra $0 */
+       nargv = xmalloc(sizeof(*nargv) * (argc + 1 + 3));
+
+       if (argc < 1)
+               die("BUG: shell command is empty");
+
+       if (strcspn(argv[0], "|&;<>()$`\\\"' \t\n*?[#~=%") != strlen(argv[0])) {
+               nargv[nargc++] = "sh";
+               nargv[nargc++] = "-c";
+
+               if (argc < 2)
+                       nargv[nargc++] = argv[0];
+               else {
+                       struct strbuf arg0 = STRBUF_INIT;
+                       strbuf_addf(&arg0, "%s \"$@\"", argv[0]);
+                       nargv[nargc++] = strbuf_detach(&arg0, NULL);
+               }
+       }
+
+       for (argc = 0; argv[argc]; argc++)
+               nargv[nargc++] = argv[argc];
+       nargv[nargc] = NULL;
+
+       return nargv;
+}
+
+#ifndef WIN32
+static int execv_shell_cmd(const char **argv)
+{
+       const char **nargv = prepare_shell_cmd(argv);
+       trace_argv_printf(nargv, "trace: exec:");
+       execvp(nargv[0], (char **)nargv);
+       free(nargv);
+       return -1;
+}
+#endif
 
 int start_command(struct child_process *cmd)
 {
@@ -123,6 +169,8 @@ fail_pipe:
                        cmd->preexec_cb();
                if (cmd->git_cmd) {
                        execv_git_cmd(cmd->argv);
+               } else if (cmd->use_shell) {
+                       execv_shell_cmd(cmd->argv);
                } else {
                        execvp(cmd->argv[0], (char *const*) cmd->argv);
                }
@@ -135,42 +183,30 @@ fail_pipe:
                        strerror(failed_errno = errno));
 #else
 {
-       int s0 = -1, s1 = -1, s2 = -1;  /* backups of stdin, stdout, stderr */
+       int fhin = 0, fhout = 1, fherr = 2;
        const char **sargv = cmd->argv;
        char **env = environ;
 
-       if (cmd->no_stdin) {
-               s0 = dup(0);
-               dup_devnull(0);
-       } else if (need_in) {
-               s0 = dup(0);
-               dup2(fdin[0], 0);
-       } else if (cmd->in) {
-               s0 = dup(0);
-               dup2(cmd->in, 0);
-       }
-
-       if (cmd->no_stderr) {
-               s2 = dup(2);
-               dup_devnull(2);
-       } else if (need_err) {
-               s2 = dup(2);
-               dup2(fderr[1], 2);
-       }
-
-       if (cmd->no_stdout) {
-               s1 = dup(1);
-               dup_devnull(1);
-       } else if (cmd->stdout_to_stderr) {
-               s1 = dup(1);
-               dup2(2, 1);
-       } else if (need_out) {
-               s1 = dup(1);
-               dup2(fdout[1], 1);
-       } else if (cmd->out > 1) {
-               s1 = dup(1);
-               dup2(cmd->out, 1);
-       }
+       if (cmd->no_stdin)
+               fhin = open("/dev/null", O_RDWR);
+       else if (need_in)
+               fhin = dup(fdin[0]);
+       else if (cmd->in)
+               fhin = dup(cmd->in);
+
+       if (cmd->no_stderr)
+               fherr = open("/dev/null", O_RDWR);
+       else if (need_err)
+               fherr = dup(fderr[1]);
+
+       if (cmd->no_stdout)
+               fhout = open("/dev/null", O_RDWR);
+       else if (cmd->stdout_to_stderr)
+               fhout = dup(fherr);
+       else if (need_out)
+               fhout = dup(fdout[1]);
+       else if (cmd->out > 1)
+               fhout = dup(cmd->out);
 
        if (cmd->dir)
                die("chdir in start_command() not implemented");
@@ -179,9 +215,12 @@ fail_pipe:
 
        if (cmd->git_cmd) {
                cmd->argv = prepare_git_cmd(cmd->argv);
+       } else if (cmd->use_shell) {
+               cmd->argv = prepare_shell_cmd(cmd->argv);
        }
 
-       cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env);
+       cmd->pid = mingw_spawnvpe(cmd->argv[0], cmd->argv, env,
+                                 fhin, fhout, fherr);
        failed_errno = errno;
        if (cmd->pid < 0 && (!cmd->silent_exec_failure || errno != ENOENT))
                error("cannot spawn %s: %s", cmd->argv[0], strerror(errno));
@@ -192,12 +231,12 @@ fail_pipe:
                free(cmd->argv);
 
        cmd->argv = sargv;
-       if (s0 >= 0)
-               dup2(s0, 0), close(s0);
-       if (s1 >= 0)
-               dup2(s1, 1), close(s1);
-       if (s2 >= 0)
-               dup2(s2, 2), close(s2);
+       if (fhin != 0)
+               close(fhin);
+       if (fhout != 1)
+               close(fhout);
+       if (fherr != 2)
+               close(fherr);
 }
 #endif
 
@@ -297,6 +336,7 @@ static void prepare_run_command_v_opt(struct child_process *cmd,
        cmd->git_cmd = opt & RUN_GIT_CMD ? 1 : 0;
        cmd->stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0;
        cmd->silent_exec_failure = opt & RUN_SILENT_EXEC_FAILURE ? 1 : 0;
+       cmd->use_shell = opt & RUN_USING_SHELL ? 1 : 0;
 }
 
 int run_command_v_opt(const char **argv, int opt)
index fb342090e3cac49f41689f9610bfe2d6c87c87c1..967ba8cc09786934724132b629587419f195b245 100644 (file)
@@ -33,6 +33,7 @@ struct child_process {
        unsigned git_cmd:1; /* if this is to be git sub-command */
        unsigned silent_exec_failure:1;
        unsigned stdout_to_stderr:1;
+       unsigned use_shell:1;
        void (*preexec_cb)(void);
 };
 
@@ -46,6 +47,7 @@ extern int run_hook(const char *index_file, const char *name, ...);
 #define RUN_GIT_CMD         2  /*If this is to be git sub-command */
 #define RUN_COMMAND_STDOUT_TO_STDERR 4
 #define RUN_SILENT_EXEC_FAILURE 8
+#define RUN_USING_SHELL 16
 int run_command_v_opt(const char **argv, int opt);
 
 /*
diff --git a/setup.c b/setup.c
index 2cf0f1993718fd737cd87e79d2f4950832c59c9f..710e2f3008c79c08cdc507288881c9a58311283a 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -77,6 +77,18 @@ int check_filename(const char *prefix, const char *arg)
        die_errno("failed to stat '%s'", arg);
 }
 
+static void NORETURN die_verify_filename(const char *prefix, const char *arg)
+{
+       unsigned char sha1[20];
+       unsigned mode;
+       /* try a detailed diagnostic ... */
+       get_sha1_with_mode_1(arg, sha1, &mode, 0, prefix);
+       /* ... or fall back the most general message. */
+       die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
+           "Use '--' to separate paths from revisions", arg);
+
+}
+
 /*
  * Verify a filename that we got as an argument for a pathspec
  * entry. Note that a filename that begins with "-" never verifies
@@ -90,8 +102,7 @@ void verify_filename(const char *prefix, const char *arg)
                die("bad flag '%s' used after filename", arg);
        if (check_filename(prefix, arg))
                return;
-       die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
-           "Use '--' to separate paths from revisions", arg);
+       die_verify_filename(prefix, arg);
 }
 
 /*
@@ -252,6 +263,8 @@ static int check_repository_format_gently(int *nongit_ok)
 const char *read_gitfile_gently(const char *path)
 {
        char *buf;
+       char *dir;
+       const char *slash;
        struct stat st;
        int fd;
        size_t len;
@@ -276,9 +289,23 @@ const char *read_gitfile_gently(const char *path)
        if (len < 9)
                die("No path in gitfile: %s", path);
        buf[len] = '\0';
-       if (!is_git_directory(buf + 8))
-               die("Not a git repository: %s", buf + 8);
-       path = make_absolute_path(buf + 8);
+       dir = buf + 8;
+
+       if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) {
+               size_t pathlen = slash+1 - path;
+               size_t dirlen = pathlen + len - 8;
+               dir = xmalloc(dirlen + 1);
+               strncpy(dir, path, pathlen);
+               strncpy(dir + pathlen, buf + 8, len - 8);
+               dir[dirlen] = '\0';
+               free(buf);
+               buf = dir;
+       }
+
+       if (!is_git_directory(dir))
+               die("Not a git repository: %s", dir);
+       path = make_absolute_path(dir);
+
        free(buf);
        return path;
 }
index 63981fb3fd9cfa6cca4126eba5a964b449c62444..7086760dbedac89075f53ebbe1539b6ebbaa9b06 100644 (file)
@@ -2458,14 +2458,6 @@ int has_pack_index(const unsigned char *sha1)
        return 1;
 }
 
-int has_pack_file(const unsigned char *sha1)
-{
-       struct stat st;
-       if (stat(sha1_pack_name(sha1), &st))
-               return 0;
-       return 1;
-}
-
 int has_sha1_pack(const unsigned char *sha1)
 {
        struct pack_entry e;
index 44bb62d270739a232e87c90c05ce89fcc86bc15b..1739e9e61240c33a1e2bf110bbeeee111c380716 100644 (file)
@@ -794,6 +794,48 @@ release_return:
        return retval;
 }
 
+int get_sha1_mb(const char *name, unsigned char *sha1)
+{
+       struct commit *one, *two;
+       struct commit_list *mbs;
+       unsigned char sha1_tmp[20];
+       const char *dots;
+       int st;
+
+       dots = strstr(name, "...");
+       if (!dots)
+               return get_sha1(name, sha1);
+       if (dots == name)
+               st = get_sha1("HEAD", sha1_tmp);
+       else {
+               struct strbuf sb;
+               strbuf_init(&sb, dots - name);
+               strbuf_add(&sb, name, dots - name);
+               st = get_sha1(sb.buf, sha1_tmp);
+               strbuf_release(&sb);
+       }
+       if (st)
+               return st;
+       one = lookup_commit_reference_gently(sha1_tmp, 0);
+       if (!one)
+               return -1;
+
+       if (get_sha1(dots[3] ? (dots + 3) : "HEAD", sha1_tmp))
+               return -1;
+       two = lookup_commit_reference_gently(sha1_tmp, 0);
+       if (!two)
+               return -1;
+       mbs = get_merge_bases(one, two, 1);
+       if (!mbs || mbs->next)
+               st = -1;
+       else {
+               st = 0;
+               hashcpy(sha1, mbs->item->object.sha1);
+       }
+       free_commit_list(mbs);
+       return st;
+}
+
 /*
  * This is like "get_sha1_basic()", except it allows "sha1 expressions",
  * notably "xyz^" for "parent of xyz"
@@ -804,7 +846,96 @@ int get_sha1(const char *name, unsigned char *sha1)
        return get_sha1_with_mode(name, sha1, &unused);
 }
 
-int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
+/* Must be called only when object_name:filename doesn't exist. */
+static void diagnose_invalid_sha1_path(const char *prefix,
+                                      const char *filename,
+                                      const unsigned char *tree_sha1,
+                                      const char *object_name)
+{
+       struct stat st;
+       unsigned char sha1[20];
+       unsigned mode;
+
+       if (!prefix)
+               prefix = "";
+
+       if (!lstat(filename, &st))
+               die("Path '%s' exists on disk, but not in '%s'.",
+                   filename, object_name);
+       if (errno == ENOENT || errno == ENOTDIR) {
+               char *fullname = xmalloc(strlen(filename)
+                                            + strlen(prefix) + 1);
+               strcpy(fullname, prefix);
+               strcat(fullname, filename);
+
+               if (!get_tree_entry(tree_sha1, fullname,
+                                   sha1, &mode)) {
+                       die("Path '%s' exists, but not '%s'.\n"
+                           "Did you mean '%s:%s'?",
+                           fullname,
+                           filename,
+                           object_name,
+                           fullname);
+               }
+               die("Path '%s' does not exist in '%s'",
+                   filename, object_name);
+       }
+}
+
+/* Must be called only when :stage:filename doesn't exist. */
+static void diagnose_invalid_index_path(int stage,
+                                       const char *prefix,
+                                       const char *filename)
+{
+       struct stat st;
+       struct cache_entry *ce;
+       int pos;
+       unsigned namelen = strlen(filename);
+       unsigned fullnamelen;
+       char *fullname;
+
+       if (!prefix)
+               prefix = "";
+
+       /* Wrong stage number? */
+       pos = cache_name_pos(filename, namelen);
+       if (pos < 0)
+               pos = -pos - 1;
+       ce = active_cache[pos];
+       if (ce_namelen(ce) == namelen &&
+           !memcmp(ce->name, filename, namelen))
+               die("Path '%s' is in the index, but not at stage %d.\n"
+                   "Did you mean ':%d:%s'?",
+                   filename, stage,
+                   ce_stage(ce), filename);
+
+       /* Confusion between relative and absolute filenames? */
+       fullnamelen = namelen + strlen(prefix);
+       fullname = xmalloc(fullnamelen + 1);
+       strcpy(fullname, prefix);
+       strcat(fullname, filename);
+       pos = cache_name_pos(fullname, fullnamelen);
+       if (pos < 0)
+               pos = -pos - 1;
+       ce = active_cache[pos];
+       if (ce_namelen(ce) == fullnamelen &&
+           !memcmp(ce->name, fullname, fullnamelen))
+               die("Path '%s' is in the index, but not '%s'.\n"
+                   "Did you mean ':%d:%s'?",
+                   fullname, filename,
+                   ce_stage(ce), fullname);
+
+       if (!lstat(filename, &st))
+               die("Path '%s' exists on disk, but not in the index.", filename);
+       if (errno == ENOENT || errno == ENOTDIR)
+               die("Path '%s' does not exist (neither on disk nor in the index).",
+                   filename);
+
+       free(fullname);
+}
+
+
+int get_sha1_with_mode_1(const char *name, unsigned char *sha1, unsigned *mode, int gently, const char *prefix)
 {
        int ret, bracket_depth;
        int namelen = strlen(name);
@@ -850,6 +981,8 @@ int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
                        }
                        pos++;
                }
+               if (!gently)
+                       diagnose_invalid_index_path(stage, prefix, cp);
                return -1;
        }
        for (cp = name, bracket_depth = 0; *cp; cp++) {
@@ -862,9 +995,25 @@ int get_sha1_with_mode(const char *name, unsigned char *sha1, unsigned *mode)
        }
        if (*cp == ':') {
                unsigned char tree_sha1[20];
-               if (!get_sha1_1(name, cp-name, tree_sha1))
-                       return get_tree_entry(tree_sha1, cp+1, sha1,
-                                             mode);
+               char *object_name = NULL;
+               if (!gently) {
+                       object_name = xmalloc(cp-name+1);
+                       strncpy(object_name, name, cp-name);
+                       object_name[cp-name] = '\0';
+               }
+               if (!get_sha1_1(name, cp-name, tree_sha1)) {
+                       const char *filename = cp+1;
+                       ret = get_tree_entry(tree_sha1, filename, sha1, mode);
+                       if (!gently) {
+                               diagnose_invalid_sha1_path(prefix, filename,
+                                                          tree_sha1, object_name);
+                               free(object_name);
+                       }
+                       return ret;
+               } else {
+                       if (!gently)
+                               die("Invalid object name '%s'.", object_name);
+               }
        }
        return ret;
 }
index a6153dca278abe957254fa091fdcd8eb13b90b25..3fa81b33656a5d2d4887b949fd103920173d37a7 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -91,13 +91,6 @@ void strbuf_ltrim(struct strbuf *sb)
        sb->buf[sb->len] = '\0';
 }
 
-void strbuf_tolower(struct strbuf *sb)
-{
-       int i;
-       for (i = 0; i < sb->len; i++)
-               sb->buf[i] = tolower(sb->buf[i]);
-}
-
 struct strbuf **strbuf_split(const struct strbuf *sb, int delim)
 {
        int alloc = 2, pos = 0;
index fa07ecf094bd3ef8997958fde29199f8fb6421b9..b37f06a34b75a14dccf99a034b683f702cb4724e 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -81,7 +81,6 @@ extern void strbuf_trim(struct strbuf *);
 extern void strbuf_rtrim(struct strbuf *);
 extern void strbuf_ltrim(struct strbuf *);
 extern int strbuf_cmp(const struct strbuf *, const struct strbuf *);
-extern void strbuf_tolower(struct strbuf *);
 
 extern struct strbuf **strbuf_split(const struct strbuf *, int delim);
 extern void strbuf_list_free(struct strbuf **);
index 14bbc477decc32618139e912fda22e585d815159..6569cf607b18b84f39ebee613471f270e42cfbdc 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef PATH_LIST_H
-#define PATH_LIST_H
+#ifndef STRING_LIST_H
+#define STRING_LIST_H
 
 struct string_list_item {
        char *string;
@@ -39,4 +39,4 @@ struct string_list_item *string_list_append(const char *string, struct string_li
 void sort_string_list(struct string_list *list);
 int unsorted_string_list_has_string(struct string_list *list, const char *string);
 
-#endif /* PATH_LIST_H */
+#endif /* STRING_LIST_H */
index 86aad653b7c113106e365a9d4e4c38b3e7737457..3007f7d5a6279c14a7c29efdc752dbbc41218c76 100644 (file)
@@ -5,7 +5,7 @@
 #include "commit.h"
 #include "revision.h"
 
-int add_submodule_odb(const char *path)
+static int add_submodule_odb(const char *path)
 {
        struct strbuf objects_directory = STRBUF_INIT;
        struct alternate_object_database *alt_odb;
index 7b0a86d35776e8695423c13403c9f4fa3465017d..88601200114f857a57214a9bf0d02ffd39a83504 100644 (file)
@@ -179,37 +179,6 @@ static int lstat_cache(struct cache_def *cache, const char *name, int len,
        return ret_flags;
 }
 
-/*
- * Invalidate the given 'name' from the cache, if 'name' matches
- * completely with the cache.
- */
-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,
-                                      &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;
-               } else {
-                       reset_lstat_cache(cache);
-               }
-       }
-}
-
-/*
- * Completely clear the contents of the cache
- */
-void clear_lstat_cache(void)
-{
-       struct cache_def *cache = &default_cache;       /* FIXME */
-       reset_lstat_cache(cache);
-}
-
 #define USE_ONLY_LSTAT  0
 
 /*
index 4e1d7dd1833f43404a00b41d55800b9fe27d1807..dcd3ebb5f2dcdbf15ca0e4a043b45cd2fc36cbb5 100644 (file)
--- a/t/README
+++ b/t/README
@@ -75,6 +75,15 @@ appropriately before running "make".
        As the names depend on the tests' file names, it is safe to
        run the tests with this option in parallel.
 
+--with-dashes::
+       By default tests are run without dashed forms of
+       commands (like git-commit) in the PATH (it only uses
+       wrappers from ../bin-wrappers).  Use this option to include
+       the build directory (..) in the PATH, which contains all
+       the dashed forms of commands.  This option is currently
+       implied by other options like --valgrind and
+       GIT_TEST_INSTALLED.
+
 You can also set the GIT_TEST_INSTALLED environment variable to
 the bindir of an existing git installation to test that installation.
 You still need to have built this git sandbox, from which various
index 6765b08065e8959dcec38940188818b952d2fcd0..28aff887b5a92ec5919f4005010ef64f85d908e9 100644 (file)
@@ -12,16 +12,29 @@ fi
 
 HTTPD_PARA=""
 
+for DEFAULT_HTTPD_PATH in '/usr/sbin/httpd' '/usr/sbin/apache2'
+do
+       if test -x "$DEFAULT_HTTPD_PATH"
+       then
+               break
+       fi
+done
+
+for DEFAULT_HTTPD_MODULE_PATH in '/usr/libexec/apache2' \
+                                '/usr/lib/apache2/modules' \
+                                '/usr/lib64/httpd/modules' \
+                                '/usr/lib/httpd/modules'
+do
+       if test -d "$DEFAULT_HTTPD_MODULE_PATH"
+       then
+               break
+       fi
+done
+
 case $(uname) in
        Darwin)
-               DEFAULT_HTTPD_PATH='/usr/sbin/httpd'
-               DEFAULT_HTTPD_MODULE_PATH='/usr/libexec/apache2'
                HTTPD_PARA="$HTTPD_PARA -DDarwin"
        ;;
-       *)
-               DEFAULT_HTTPD_PATH='/usr/sbin/apache2'
-               DEFAULT_HTTPD_MODULE_PATH='/usr/lib/apache2/modules'
-       ;;
 esac
 
 LIB_HTTPD_PATH=${LIB_HTTPD_PATH-"$DEFAULT_HTTPD_PATH"}
@@ -49,6 +62,11 @@ then
                        say "skipping test, at least Apache version 2 is required"
                        test_done
                fi
+               if ! test -d "$DEFAULT_HTTPD_MODULE_PATH"
+               then
+                       say "Apache module directory not found.  Skipping tests."
+                       test_done
+               fi
 
                LIB_HTTPD_MODULE_PATH="$DEFAULT_HTTPD_MODULE_PATH"
        fi
index 0fe3fd0d012159f6abe7f7d8b006d3f8e59cbab3..4961505d1dd99da529b43abd42522e6a005961b1 100644 (file)
@@ -22,8 +22,13 @@ Alias /dumb/ www/
 
 <Location /smart/>
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+       SetEnv GIT_HTTP_EXPORT_ALL
+</Location>
+<Location /smart_noexport/>
+       SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
 </Location>
 ScriptAlias /smart/ ${GIT_EXEC_PATH}/git-http-backend/
+ScriptAlias /smart_noexport/ ${GIT_EXEC_PATH}/git-http-backend/
 <Directory ${GIT_EXEC_PATH}>
        Options None
 </Directory>
index 8fc39d77cec6168dae930beef785597dace24aa3..6cb8d60ea2649495c0e3c8bbb8b7cc75c36799b7 100755 (executable)
@@ -4,7 +4,8 @@ test_description='blob conversion via gitattributes'
 
 . ./test-lib.sh
 
-cat <<\EOF >rot13.sh
+cat <<EOF >rot13.sh
+#!$SHELL_PATH
 tr \
   'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' \
   'nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
diff --git a/t/t1011-read-tree-sparse-checkout.sh b/t/t1011-read-tree-sparse-checkout.sh
new file mode 100755 (executable)
index 0000000..62246db
--- /dev/null
@@ -0,0 +1,150 @@
+#!/bin/sh
+
+test_description='sparse checkout tests'
+
+. ./test-lib.sh
+
+cat >expected <<EOF
+100644 77f0ba1734ed79d12881f81b36ee134de6a3327b 0      init.t
+100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0      sub/added
+EOF
+test_expect_success 'setup' '
+       test_commit init &&
+       echo modified >> init.t &&
+       mkdir sub &&
+       touch sub/added &&
+       git add init.t sub/added &&
+       git commit -m "modified and added" &&
+       git tag top &&
+       git rm sub/added &&
+       git commit -m removed &&
+       git tag removed &&
+       git checkout top &&
+       git ls-files --stage > result &&
+       test_cmp expected result
+'
+
+cat >expected.swt <<EOF
+H init.t
+H sub/added
+EOF
+test_expect_success 'read-tree without .git/info/sparse-checkout' '
+       git read-tree -m -u HEAD &&
+       git ls-files --stage > result &&
+       test_cmp expected result &&
+       git ls-files -t > result &&
+       test_cmp expected.swt result
+'
+
+test_expect_success 'read-tree with .git/info/sparse-checkout but disabled' '
+       echo > .git/info/sparse-checkout
+       git read-tree -m -u HEAD &&
+       git ls-files -t > result &&
+       test_cmp expected.swt result &&
+       test -f init.t &&
+       test -f sub/added
+'
+
+test_expect_success 'read-tree --no-sparse-checkout with empty .git/info/sparse-checkout and enabled' '
+       git config core.sparsecheckout true &&
+       echo > .git/info/sparse-checkout &&
+       git read-tree --no-sparse-checkout -m -u HEAD &&
+       git ls-files -t > result &&
+       test_cmp expected.swt result &&
+       test -f init.t &&
+       test -f sub/added
+'
+
+test_expect_success 'read-tree with empty .git/info/sparse-checkout' '
+       git config core.sparsecheckout true &&
+       echo > .git/info/sparse-checkout &&
+       test_must_fail git read-tree -m -u HEAD &&
+       git ls-files --stage > result &&
+       test_cmp expected result &&
+       git ls-files -t > result &&
+       test_cmp expected.swt result &&
+       test -f init.t &&
+       test -f sub/added
+'
+
+cat >expected.swt <<EOF
+S init.t
+H sub/added
+EOF
+test_expect_success 'match directories with trailing slash' '
+       echo sub/ > .git/info/sparse-checkout &&
+       git read-tree -m -u HEAD &&
+       git ls-files -t > result &&
+       test_cmp expected.swt result &&
+       test ! -f init.t &&
+       test -f sub/added
+'
+
+cat >expected.swt <<EOF
+H init.t
+H sub/added
+EOF
+test_expect_failure 'match directories without trailing slash' '
+       echo init.t > .git/info/sparse-checkout &&
+       echo sub >> .git/info/sparse-checkout &&
+       git read-tree -m -u HEAD &&
+       git ls-files -t > result &&
+       test_cmp expected.swt result &&
+       test ! -f init.t &&
+       test -f sub/added
+'
+
+cat >expected.swt <<EOF
+H init.t
+S sub/added
+EOF
+test_expect_success 'checkout area changes' '
+       echo init.t > .git/info/sparse-checkout &&
+       git read-tree -m -u HEAD &&
+       git ls-files -t > result &&
+       test_cmp expected.swt result &&
+       test -f init.t &&
+       test ! -f sub/added
+'
+
+test_expect_success 'read-tree updates worktree, absent case' '
+       echo sub/added > .git/info/sparse-checkout &&
+       git checkout -f top &&
+       git read-tree -m -u HEAD^ &&
+       test ! -f init.t
+'
+
+test_expect_success 'read-tree updates worktree, dirty case' '
+       echo sub/added > .git/info/sparse-checkout &&
+       git checkout -f top &&
+       echo dirty > init.t &&
+       git read-tree -m -u HEAD^ &&
+       grep -q dirty init.t &&
+       rm init.t
+'
+
+test_expect_success 'read-tree removes worktree, dirty case' '
+       echo init.t > .git/info/sparse-checkout &&
+       git checkout -f top &&
+       echo dirty > added &&
+       git read-tree -m -u HEAD^ &&
+       grep -q dirty added
+'
+
+test_expect_success 'read-tree adds to worktree, absent case' '
+       echo init.t > .git/info/sparse-checkout &&
+       git checkout -f removed &&
+       git read-tree -u -m HEAD^ &&
+       test ! -f sub/added
+'
+
+test_expect_success 'read-tree adds to worktree, dirty case' '
+       echo init.t > .git/info/sparse-checkout &&
+       git checkout -f removed &&
+       mkdir sub &&
+       echo dirty > sub/added &&
+       git read-tree -u -m HEAD^ &&
+       grep -q dirty sub/added
+'
+
+test_done
index 83b7294010cd59d5438b6020868c699c01105595..f89d7e9e4959ddccafafd11c5fbf9f4b06541578 100755 (executable)
@@ -683,6 +683,34 @@ test_expect_success 'set --bool-or-int' '
 
 rm .git/config
 
+cat >expect <<\EOF
+[path]
+       home = ~/
+       normal = /dev/null
+       trailingtilde = foo~
+EOF
+
+test_expect_success 'set --path' '
+       git config --path path.home "~/" &&
+       git config --path path.normal "/dev/null" &&
+       git config --path path.trailingtilde "foo~" &&
+       test_cmp expect .git/config'
+
+cat >expect <<EOF
+$HOME/
+/dev/null
+foo~
+EOF
+
+test_expect_success 'get --path' '
+       git config --get --path path.home > result &&
+       git config --get --path path.normal >> result &&
+       git config --get --path path.trailingtilde >> result &&
+       test_cmp expect result
+'
+
+rm .git/config
+
 git config quote.leading " test"
 git config quote.ending "test "
 git config quote.semicolon "test;test"
diff --git a/t/t1506-rev-parse-diagnosis.sh b/t/t1506-rev-parse-diagnosis.sh
new file mode 100755 (executable)
index 0000000..af721f9
--- /dev/null
@@ -0,0 +1,69 @@
+#!/bin/sh
+
+test_description='test git rev-parse diagnosis for invalid argument'
+
+exec </dev/null
+
+. ./test-lib.sh
+
+HASH_file=
+
+test_expect_success 'set up basic repo' '
+       echo one > file.txt &&
+       mkdir subdir &&
+       echo two > subdir/file.txt &&
+       echo three > subdir/file2.txt &&
+       git add . &&
+       git commit -m init &&
+       echo four > index-only.txt &&
+       git add index-only.txt &&
+       echo five > disk-only.txt
+'
+
+test_expect_success 'correct file objects' '
+       HASH_file=$(git rev-parse HEAD:file.txt) &&
+       git rev-parse HEAD:subdir/file.txt &&
+       git rev-parse :index-only.txt &&
+       (cd subdir &&
+        git rev-parse HEAD:subdir/file2.txt &&
+        test $HASH_file = $(git rev-parse HEAD:file.txt) &&
+        test $HASH_file = $(git rev-parse :file.txt) &&
+        test $HASH_file = $(git rev-parse :0:file.txt) )
+'
+
+test_expect_success 'incorrect revision id' '
+       test_must_fail git rev-parse foobar:file.txt 2>error &&
+       grep "Invalid object name '"'"'foobar'"'"'." error &&
+       test_must_fail git rev-parse foobar 2> error &&
+       grep "unknown revision or path not in the working tree." error
+'
+
+test_expect_success 'incorrect file in sha1:path' '
+       test_must_fail git rev-parse HEAD:nothing.txt 2> error &&
+       grep "fatal: Path '"'"'nothing.txt'"'"' does not exist in '"'"'HEAD'"'"'" error &&
+       test_must_fail git rev-parse HEAD:index-only.txt 2> error &&
+       grep "fatal: Path '"'"'index-only.txt'"'"' exists on disk, but not in '"'"'HEAD'"'"'." error &&
+       (cd subdir &&
+        test_must_fail git rev-parse HEAD:file2.txt 2> error &&
+        grep "Did you mean '"'"'HEAD:subdir/file2.txt'"'"'?" error )
+'
+
+test_expect_success 'incorrect file in :path and :N:path' '
+       test_must_fail git rev-parse :nothing.txt 2> error &&
+       grep "fatal: Path '"'"'nothing.txt'"'"' does not exist (neither on disk nor in the index)." error &&
+       test_must_fail git rev-parse :1:nothing.txt 2> error &&
+       grep "Path '"'"'nothing.txt'"'"' does not exist (neither on disk nor in the index)." error &&
+       test_must_fail git rev-parse :1:file.txt 2> error &&
+       grep "Did you mean '"'"':0:file.txt'"'"'?" error &&
+       (cd subdir &&
+        test_must_fail git rev-parse :1:file.txt 2> error &&
+        grep "Did you mean '"'"':0:file.txt'"'"'?" error &&
+        test_must_fail git rev-parse :file2.txt 2> error &&
+        grep "Did you mean '"'"':0:subdir/file2.txt'"'"'?" error &&
+        test_must_fail git rev-parse :2:file2.txt 2> error &&
+        grep "Did you mean '"'"':0:subdir/file2.txt'"'"'?" error) &&
+       test_must_fail git rev-parse :disk-only.txt 2> error &&
+       grep "fatal: Path '"'"'disk-only.txt'"'"' exists on disk, but not in the index." error
+'
+
+test_done
index 87b30a268c66848726baec5676e73b8db686806d..b44de9dc623fc923941b61719f445170602e21be 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='checkout can switch to last branch'
+test_description='checkout can switch to last branch and merge base'
 
 . ./test-lib.sh
 
@@ -91,4 +91,29 @@ test_expect_success 'switch to twelfth from the last' '
        test "z$(git symbolic-ref HEAD)" = "zrefs/heads/branch13"
 '
 
+test_expect_success 'merge base test setup' '
+       git checkout -b another other &&
+       echo "hello again" >>world &&
+       git add world &&
+       git commit -m third
+'
+
+test_expect_success 'another...master' '
+       git checkout another &&
+       git checkout another...master &&
+       test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+'
+
+test_expect_success '...master' '
+       git checkout another &&
+       git checkout ...master &&
+       test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+'
+
+test_expect_success 'master...' '
+       git checkout another &&
+       git checkout master... &&
+       test "z$(git rev-parse --verify HEAD)" = "z$(git rev-parse --verify master^)"
+'
+
 test_done
diff --git a/t/t2104-update-index-gitfile.sh b/t/t2104-update-index-gitfile.sh
new file mode 100755 (executable)
index 0000000..641607d
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Brad King
+#
+
+test_description='git update-index for gitlink to .git file.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'submodule with absolute .git file' '
+       mkdir sub1 &&
+       (cd sub1 &&
+        git init &&
+        REAL="$(pwd)/.real" &&
+        mv .git "$REAL"
+        echo "gitdir: $REAL" >.git &&
+        test_commit first)
+'
+
+test_expect_success 'add gitlink to absolute .git file' '
+       git update-index --add -- sub1
+'
+
+test_expect_success 'submodule with relative .git file' '
+       mkdir sub2 &&
+       (cd sub2 &&
+        git init &&
+        mv .git .real &&
+        echo "gitdir: .real" >.git &&
+        test_commit first)
+'
+
+test_expect_success 'add gitlink to relative .git file' '
+       git update-index --add -- sub2
+'
+
+test_done
diff --git a/t/t2104-update-index-skip-worktree.sh b/t/t2104-update-index-skip-worktree.sh
new file mode 100755 (executable)
index 0000000..1d0879b
--- /dev/null
@@ -0,0 +1,57 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Nguyễn Thái Ngọc Duy
+#
+
+test_description='skip-worktree bit test'
+
+. ./test-lib.sh
+
+cat >expect.full <<EOF
+H 1
+H 2
+H sub/1
+H sub/2
+EOF
+
+cat >expect.skip <<EOF
+S 1
+H 2
+S sub/1
+H sub/2
+EOF
+
+test_expect_success 'setup' '
+       mkdir sub &&
+       touch ./1 ./2 sub/1 sub/2 &&
+       git add 1 2 sub/1 sub/2 &&
+       git ls-files -t | test_cmp expect.full -
+'
+
+test_expect_success 'index is at version 2' '
+       test "$(test-index-version < .git/index)" = 2
+'
+
+test_expect_success 'update-index --skip-worktree' '
+       git update-index --skip-worktree 1 sub/1 &&
+       git ls-files -t | test_cmp expect.skip -
+'
+
+test_expect_success 'index is at version 3 after having some skip-worktree entries' '
+       test "$(test-index-version < .git/index)" = 3
+'
+
+test_expect_success 'ls-files -t' '
+       git ls-files -t | test_cmp expect.skip -
+'
+
+test_expect_success 'update-index --no-skip-worktree' '
+       git update-index --no-skip-worktree 1 sub/1 &&
+       git ls-files -t | test_cmp expect.full -
+'
+
+test_expect_success 'index version is back to 2 when there is no skip-worktree entry' '
+       test "$(test-index-version < .git/index)" = 2
+'
+
+test_done
index c65bca838881938e2f924cfe62b2c303dbd5b1cd..132c4765cbe8ffc8b92deb5d3e6ca05c012d7c37 100755 (executable)
@@ -64,6 +64,8 @@ two/*.4
 echo '!*.2
 !*.8' >one/two/.gitignore
 
+allignores='.gitignore one/.gitignore one/two/.gitignore'
+
 test_expect_success \
     'git ls-files --others with various exclude options.' \
     'git ls-files --others \
@@ -85,6 +87,26 @@ test_expect_success \
        >output &&
      test_cmp expect output'
 
+test_expect_success 'setup skip-worktree gitignore' '
+       git add $allignores &&
+       git update-index --skip-worktree $allignores &&
+       rm $allignores
+'
+
+test_expect_success \
+    'git ls-files --others with various exclude options.' \
+    'git ls-files --others \
+       --exclude=\*.6 \
+       --exclude-per-directory=.gitignore \
+       --exclude-from=.git/ignore \
+       >output &&
+     test_cmp expect output'
+
+test_expect_success 'restore gitignore' '
+       git checkout $allignores &&
+       rm .git/index
+'
+
 cat > excludes-file <<\EOF
 *.[1-8]
 e*
diff --git a/t/t3415-rebase-onto-threedots.sh b/t/t3415-rebase-onto-threedots.sh
new file mode 100755 (executable)
index 0000000..ddf2f64
--- /dev/null
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+test_description='git rebase --onto A...B'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-rebase.sh"
+
+# Rebase only the tip commit of "topic" on merge base between "master"
+# and "topic".  Cannot do this for "side" with "master" because there
+# is no single merge base.
+#
+#
+#          F---G topic                             G'
+#         /                                       /
+# A---B---C---D---E master      -->       A---B---C---D---E
+#      \   \ /
+#      \   x
+#       \ / \
+#        H---I---J---K side
+
+test_expect_success setup '
+       test_commit A &&
+       test_commit B &&
+       git branch side &&
+       test_commit C &&
+       git branch topic &&
+       git checkout side &&
+       test_commit H &&
+       git checkout master &&
+       test_tick &&
+       git merge H &&
+       git tag D &&
+       test_commit E &&
+       git checkout topic &&
+       test_commit F &&
+       test_commit G &&
+       git checkout side &&
+       test_tick &&
+       git merge C &&
+       git tag I &&
+       test_commit J &&
+       test_commit K
+'
+
+test_expect_success 'rebase --onto master...topic' '
+       git reset --hard &&
+       git checkout topic &&
+       git reset --hard G &&
+
+       git rebase --onto master...topic F &&
+       git rev-parse HEAD^1 >actual &&
+       git rev-parse C^0 >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --onto master...' '
+       git reset --hard &&
+       git checkout topic &&
+       git reset --hard G &&
+
+       git rebase --onto master... F &&
+       git rev-parse HEAD^1 >actual &&
+       git rev-parse C^0 >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --onto master...side' '
+       git reset --hard &&
+       git checkout side &&
+       git reset --hard K &&
+
+       test_must_fail git rebase --onto master...side J
+'
+
+test_expect_success 'rebase -i --onto master...topic' '
+       git reset --hard &&
+       git checkout topic &&
+       git reset --hard G &&
+       set_fake_editor &&
+       EXPECT_COUNT=1 git rebase -i --onto master...topic F &&
+       git rev-parse HEAD^1 >actual &&
+       git rev-parse C^0 >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase -i --onto master...' '
+       git reset --hard &&
+       git checkout topic &&
+       git reset --hard G &&
+       set_fake_editor &&
+       EXPECT_COUNT=1 git rebase -i --onto master... F &&
+       git rev-parse HEAD^1 >actual &&
+       git rev-parse C^0 >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase -i --onto master...side' '
+       git reset --hard &&
+       git checkout side &&
+       git reset --hard K &&
+
+       test_must_fail git rebase -i --onto master...side J
+'
+
+test_done
index a3f0897a52ce2147388baeac6fc64d3b8501b516..88c5619ae7471ab0d3286259d88c437ae3953b4a 100755 (executable)
@@ -48,7 +48,7 @@ test_expect_success 'file is considered binary by plumbing' '
 
 test_expect_success 'setup textconv filters' '
        echo file diff=foo >.gitattributes &&
-       git config diff.foo.textconv "$PWD"/hexdump &&
+       git config diff.foo.textconv "\"$(pwd)\""/hexdump &&
        git config diff.fail.textconv false
 '
 
index a894c6062271c53b688830a3230d1c78fb561d70..7e7b307a24606131b4880817a0056af11973f3d2 100755 (executable)
@@ -54,7 +54,7 @@ chmod +x dump
 
 test_expect_success 'setup textconv' '
        echo file diff=foo >.gitattributes &&
-       git config diff.foo.textconv "$PWD"/dump
+       git config diff.foo.textconv "\"$(pwd)\""/dump
 '
 
 test_expect_success 'rewrite diff respects textconv' '
index a6bc028a57115729d38e4b228cd259880d0bf6f8..bb402c3780356d1feab4e8b7c9b9624495d3e176 100755 (executable)
@@ -217,7 +217,22 @@ test_expect_success 'rerere.autoupdate' '
        git checkout version2 &&
        test_must_fail git merge fifth &&
        test 0 = $(git ls-files -u | wc -l)
+'
 
+test_expect_success 'merge --rerere-autoupdate' '
+       git config --unset rerere.autoupdate
+       git reset --hard &&
+       git checkout version2 &&
+       test_must_fail git merge --rerere-autoupdate fifth &&
+       test 0 = $(git ls-files -u | wc -l)
+'
+
+test_expect_success 'merge --no-rerere-autoupdate' '
+       git config rerere.autoupdate true
+       git reset --hard &&
+       git checkout version2 &&
+       test_must_fail git merge --no-rerere-autoupdate fifth &&
+       test 2 = $(git ls-files -u | wc -l)
 '
 
 test_done
index 5858b868ed6ff05d458996cf8c359a7148591582..d05a9138b46eec072750142f912a41caa3e23d83 100755 (executable)
@@ -7,19 +7,19 @@ test_description='Test the post-checkout hook.'
 . ./test-lib.sh
 
 test_expect_success setup '
-        echo Data for commit0. >a &&
-        echo Data for commit0. >b &&
-        git update-index --add a &&
-        git update-index --add b &&
-        tree0=$(git write-tree) &&
-        commit0=$(echo setup | git commit-tree $tree0) &&
-        git update-ref refs/heads/master $commit0 &&
-        git clone ./. clone1 &&
-        git clone ./. clone2 &&
-        GIT_DIR=clone2/.git git branch -a new2 &&
-        echo Data for commit1. >clone2/b &&
-        GIT_DIR=clone2/.git git add clone2/b &&
-        GIT_DIR=clone2/.git git commit -m new2
+       echo Data for commit0. >a &&
+       echo Data for commit0. >b &&
+       git update-index --add a &&
+       git update-index --add b &&
+       tree0=$(git write-tree) &&
+       commit0=$(echo setup | git commit-tree $tree0) &&
+       git update-ref refs/heads/master $commit0 &&
+       git clone ./. clone1 &&
+       git clone ./. clone2 &&
+       GIT_DIR=clone2/.git git branch new2 &&
+       echo Data for commit1. >clone2/b &&
+       GIT_DIR=clone2/.git git add clone2/b &&
+       GIT_DIR=clone2/.git git commit -m new2
 '
 
 for clone in 1 2; do
index fd166d9de356dafb000504506285a6f77fcc0a37..936fe0a1a635bc8a553962f13bff32e873c358cd 100755 (executable)
@@ -419,6 +419,20 @@ test_expect_success 'update default (overridden, with funny whitespace)' '
 
 '
 
+test_expect_success 'update (with remotes.default defined)' '
+
+       (cd one &&
+        for b in $(git branch -r)
+        do
+               git branch -r -d $b || break
+        done &&
+        git config remotes.default "drosophila" &&
+        git remote update &&
+        git branch -r > output &&
+        test_cmp expect output)
+
+'
+
 test_expect_success '"remote show" does not show symbolic refs' '
 
        git clone one three &&
index 516127b5390bafbd725564e91fb24ead224cf3a7..0f04b2e8949dfec46fbc42af8347c9f3e6d302a7 100755 (executable)
@@ -547,6 +547,32 @@ test_expect_success 'allow deleting an invalid remote ref' '
 
 '
 
+test_expect_success 'allow deleting a ref using --delete' '
+       mk_test heads/master &&
+       (cd testrepo && git config receive.denyDeleteCurrent warn) &&
+       git push testrepo --delete master &&
+       (cd testrepo && test_must_fail git rev-parse --verify refs/heads/master)
+'
+
+test_expect_success 'allow deleting a tag using --delete' '
+       mk_test heads/master &&
+       git tag -a -m dummy_message deltag heads/master &&
+       git push testrepo --tags &&
+       (cd testrepo && git rev-parse --verify -q refs/tags/deltag) &&
+       git push testrepo --delete tag deltag &&
+       (cd testrepo && test_must_fail git rev-parse --verify refs/tags/deltag)
+'
+
+test_expect_success 'push --delete without args aborts' '
+       mk_test heads/master &&
+       test_must_fail git push testrepo --delete
+'
+
+test_expect_success 'push --delete refuses src:dest refspecs' '
+       mk_test heads/master &&
+       test_must_fail git push testrepo --delete master:foo
+'
+
 test_expect_success 'warn on push to HEAD of non-bare repository' '
        mk_test heads/master
        (cd testrepo &&
index c0505ecd7be7c63f3d33efc28744e251600bc3fb..7faa31a299f263b0628d4a23e4500d3e43e0fdda 100755 (executable)
@@ -38,7 +38,7 @@ cat >exp <<EOF
 > POST /smart/repo.git/git-upload-pack HTTP/1.1
 > Accept-Encoding: deflate, gzip
 > Content-Type: application/x-git-upload-pack-request
-> Accept: application/x-git-upload-pack-response
+> Accept: application/x-git-upload-pack-result
 > Content-Length: xxx
 < HTTP/1.1 200 OK
 < Pragma: no-cache
diff --git a/t/t5560-http-backend-noserver.sh b/t/t5560-http-backend-noserver.sh
new file mode 100755 (executable)
index 0000000..44885b8
--- /dev/null
@@ -0,0 +1,73 @@
+#!/bin/sh
+
+test_description='test git-http-backend-noserver'
+. ./test-lib.sh
+
+HTTPD_DOCUMENT_ROOT_PATH="$TRASH_DIRECTORY"
+
+run_backend() {
+       echo "$2" |
+       QUERY_STRING="${1#*\?}" \
+       GIT_PROJECT_ROOT="$HTTPD_DOCUMENT_ROOT_PATH" \
+       PATH_INFO="${1%%\?*}" \
+       git http-backend >act.out 2>act.err
+}
+
+GET() {
+       export REQUEST_METHOD="GET" &&
+       run_backend "/repo.git/$1" &&
+       unset REQUEST_METHOD &&
+       if ! grep "Status" act.out >act
+       then
+               printf "Status: 200 OK\r\n" >act
+       fi
+       printf "Status: $2\r\n" >exp &&
+       test_cmp exp act
+}
+
+POST() {
+       export REQUEST_METHOD="POST" &&
+       export CONTENT_TYPE="application/x-$1-request" &&
+       run_backend "/repo.git/$1" "$2" &&
+       unset REQUEST_METHOD &&
+       unset CONTENT_TYPE &&
+       if ! grep "Status" act.out >act
+       then
+               printf "Status: 200 OK\r\n" >act
+       fi
+       printf "Status: $3\r\n" >exp &&
+       test_cmp exp act
+}
+
+log_div() {
+       return 0
+}
+
+. "$TEST_DIRECTORY"/t556x_common
+
+expect_aliased() {
+       export REQUEST_METHOD="GET" &&
+       if test $1 = 0; then
+               run_backend "$2"
+       else
+               run_backend "$2" &&
+               echo "fatal: '$2': aliased" >exp.err &&
+               test_cmp exp.err act.err
+       fi
+       unset REQUEST_METHOD
+}
+
+test_expect_success 'http-backend blocks bad PATH_INFO' '
+       config http.getanyfile true &&
+
+       expect_aliased 0 /repo.git/HEAD &&
+
+       expect_aliased 1 /repo.git/../HEAD &&
+       expect_aliased 1 /../etc/passwd &&
+       expect_aliased 1 ../etc/passwd &&
+       expect_aliased 1 /etc//passwd &&
+       expect_aliased 1 /etc/./passwd &&
+       expect_aliased 1 //domain/data.txt
+'
+
+test_done
diff --git a/t/t5560-http-backend.sh b/t/t5560-http-backend.sh
deleted file mode 100755 (executable)
index ed034bc..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-#!/bin/sh
-
-test_description='test git-http-backend'
-. ./test-lib.sh
-
-if test -n "$NO_CURL"; then
-       say 'skipping test, git built without http support'
-       test_done
-fi
-
-LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5560'}
-. "$TEST_DIRECTORY"/lib-httpd.sh
-start_httpd
-
-find_file() {
-       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       find $1 -type f |
-       sed -e 1q
-}
-
-config() {
-       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" config $1 $2
-}
-
-GET() {
-       curl --include "$HTTPD_URL/smart/repo.git/$1" >out 2>/dev/null &&
-       tr '\015' Q <out |
-       sed '
-               s/Q$//
-               1q
-       ' >act &&
-       echo "HTTP/1.1 $2" >exp &&
-       test_cmp exp act
-}
-
-POST() {
-       curl --include --data "$2" \
-       --header "Content-Type: application/x-$1-request" \
-       "$HTTPD_URL/smart/repo.git/$1" >out 2>/dev/null &&
-       tr '\015' Q <out |
-       sed '
-               s/Q$//
-               1q
-       ' >act &&
-       echo "HTTP/1.1 $3" >exp &&
-       test_cmp exp act
-}
-
-log_div() {
-       echo >>"$HTTPD_ROOT_PATH"/access.log
-       echo "###  $1" >>"$HTTPD_ROOT_PATH"/access.log
-       echo "###" >>"$HTTPD_ROOT_PATH"/access.log
-}
-
-test_expect_success 'setup repository' '
-       echo content >file &&
-       git add file &&
-       git commit -m one &&
-
-       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-        git --bare init &&
-        : >objects/info/alternates &&
-        : >objects/info/http-alternates
-       ) &&
-       git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-       git push public master:master &&
-
-       (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
-        git repack -a -d
-       ) &&
-
-       echo other >file &&
-       git add file &&
-       git commit -m two &&
-       git push public master:master &&
-
-       LOOSE_URL=$(find_file objects/??) &&
-       PACK_URL=$(find_file objects/pack/*.pack) &&
-       IDX_URL=$(find_file objects/pack/*.idx)
-'
-
-get_static_files() {
-       GET HEAD "$1" &&
-       GET info/refs "$1" &&
-       GET objects/info/packs "$1" &&
-       GET objects/info/alternates "$1" &&
-       GET objects/info/http-alternates "$1" &&
-       GET $LOOSE_URL "$1" &&
-       GET $PACK_URL "$1" &&
-       GET $IDX_URL "$1"
-}
-
-test_expect_success 'direct refs/heads/master not found' '
-       log_div "refs/heads/master"
-       GET refs/heads/master "404 Not Found"
-'
-test_expect_success 'static file is ok' '
-       log_div "getanyfile default"
-       get_static_files "200 OK"
-'
-test_expect_success 'static file if http.getanyfile true is ok' '
-       log_div "getanyfile true"
-       config http.getanyfile true &&
-       get_static_files "200 OK"
-'
-test_expect_success 'static file if http.getanyfile false fails' '
-       log_div "getanyfile false"
-       config http.getanyfile false &&
-       get_static_files "403 Forbidden"
-'
-
-test_expect_success 'http.uploadpack default enabled' '
-       log_div "uploadpack default"
-       GET info/refs?service=git-upload-pack "200 OK"  &&
-       POST git-upload-pack 0000 "200 OK"
-'
-test_expect_success 'http.uploadpack true' '
-       log_div "uploadpack true"
-       config http.uploadpack true &&
-       GET info/refs?service=git-upload-pack "200 OK" &&
-       POST git-upload-pack 0000 "200 OK"
-'
-test_expect_success 'http.uploadpack false' '
-       log_div "uploadpack false"
-       config http.uploadpack false &&
-       GET info/refs?service=git-upload-pack "403 Forbidden" &&
-       POST git-upload-pack 0000 "403 Forbidden"
-'
-
-test_expect_success 'http.receivepack default disabled' '
-       log_div "receivepack default"
-       GET info/refs?service=git-receive-pack "403 Forbidden"  &&
-       POST git-receive-pack 0000 "403 Forbidden"
-'
-test_expect_success 'http.receivepack true' '
-       log_div "receivepack true"
-       config http.receivepack true &&
-       GET info/refs?service=git-receive-pack "200 OK" &&
-       POST git-receive-pack 0000 "200 OK"
-'
-test_expect_success 'http.receivepack false' '
-       log_div "receivepack false"
-       config http.receivepack false &&
-       GET info/refs?service=git-receive-pack "403 Forbidden" &&
-       POST git-receive-pack 0000 "403 Forbidden"
-'
-
-run_backend() {
-       REQUEST_METHOD=GET \
-       GIT_PROJECT_ROOT="$HTTPD_DOCUMENT_ROOT_PATH" \
-       PATH_INFO="$2" \
-       git http-backend >act.out 2>act.err
-}
-
-path_info() {
-       if test $1 = 0; then
-               run_backend "$2"
-       else
-               test_must_fail run_backend "$2" &&
-               echo "fatal: '$2': aliased" >exp.err &&
-               test_cmp exp.err act.err
-       fi
-}
-
-test_expect_success 'http-backend blocks bad PATH_INFO' '
-       config http.getanyfile true &&
-
-       run_backend 0 /repo.git/HEAD &&
-
-       run_backend 1 /repo.git/../HEAD &&
-       run_backend 1 /../etc/passwd &&
-       run_backend 1 ../etc/passwd &&
-       run_backend 1 /etc//passwd &&
-       run_backend 1 /etc/./passwd &&
-       run_backend 1 /etc/.../passwd &&
-       run_backend 1 //domain/data.txt
-'
-
-cat >exp <<EOF
-
-###  refs/heads/master
-###
-GET  /smart/repo.git/refs/heads/master HTTP/1.1 404 -
-
-###  getanyfile default
-###
-GET  /smart/repo.git/HEAD HTTP/1.1 200
-GET  /smart/repo.git/info/refs HTTP/1.1 200
-GET  /smart/repo.git/objects/info/packs HTTP/1.1 200
-GET  /smart/repo.git/objects/info/alternates HTTP/1.1 200 -
-GET  /smart/repo.git/objects/info/http-alternates HTTP/1.1 200 -
-GET  /smart/repo.git/$LOOSE_URL HTTP/1.1 200
-GET  /smart/repo.git/$PACK_URL HTTP/1.1 200
-GET  /smart/repo.git/$IDX_URL HTTP/1.1 200
-
-###  getanyfile true
-###
-GET  /smart/repo.git/HEAD HTTP/1.1 200
-GET  /smart/repo.git/info/refs HTTP/1.1 200
-GET  /smart/repo.git/objects/info/packs HTTP/1.1 200
-GET  /smart/repo.git/objects/info/alternates HTTP/1.1 200 -
-GET  /smart/repo.git/objects/info/http-alternates HTTP/1.1 200 -
-GET  /smart/repo.git/$LOOSE_URL HTTP/1.1 200
-GET  /smart/repo.git/$PACK_URL HTTP/1.1 200
-GET  /smart/repo.git/$IDX_URL HTTP/1.1 200
-
-###  getanyfile false
-###
-GET  /smart/repo.git/HEAD HTTP/1.1 403 -
-GET  /smart/repo.git/info/refs HTTP/1.1 403 -
-GET  /smart/repo.git/objects/info/packs HTTP/1.1 403 -
-GET  /smart/repo.git/objects/info/alternates HTTP/1.1 403 -
-GET  /smart/repo.git/objects/info/http-alternates HTTP/1.1 403 -
-GET  /smart/repo.git/$LOOSE_URL HTTP/1.1 403 -
-GET  /smart/repo.git/$PACK_URL HTTP/1.1 403 -
-GET  /smart/repo.git/$IDX_URL HTTP/1.1 403 -
-
-###  uploadpack default
-###
-GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/repo.git/git-upload-pack HTTP/1.1 200 -
-
-###  uploadpack true
-###
-GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/repo.git/git-upload-pack HTTP/1.1 200 -
-
-###  uploadpack false
-###
-GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 403 -
-POST /smart/repo.git/git-upload-pack HTTP/1.1 403 -
-
-###  receivepack default
-###
-GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 403 -
-POST /smart/repo.git/git-receive-pack HTTP/1.1 403 -
-
-###  receivepack true
-###
-GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
-POST /smart/repo.git/git-receive-pack HTTP/1.1 200 -
-
-###  receivepack false
-###
-GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 403 -
-POST /smart/repo.git/git-receive-pack HTTP/1.1 403 -
-EOF
-test_expect_success 'server request log matches test results' '
-       sed -e "
-               s/^.* \"//
-               s/\"//
-               s/ [1-9][0-9]*\$//
-               s/^GET /GET  /
-       " >act <"$HTTPD_ROOT_PATH"/access.log &&
-       test_cmp exp act
-'
-
-stop_httpd
-test_done
diff --git a/t/t5561-http-backend.sh b/t/t5561-http-backend.sh
new file mode 100755 (executable)
index 0000000..8c6d0b2
--- /dev/null
@@ -0,0 +1,149 @@
+#!/bin/sh
+
+test_description='test git-http-backend'
+. ./test-lib.sh
+
+if test -n "$NO_CURL"; then
+       say 'skipping test, git built without http support'
+       test_done
+fi
+
+LIB_HTTPD_PORT=${LIB_HTTPD_PORT-'5561'}
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+GET() {
+       curl --include "$HTTPD_URL/$SMART/repo.git/$1" >out 2>/dev/null &&
+       tr '\015' Q <out |
+       sed '
+               s/Q$//
+               1q
+       ' >act &&
+       echo "HTTP/1.1 $2" >exp &&
+       test_cmp exp act
+}
+
+POST() {
+       curl --include --data "$2" \
+       --header "Content-Type: application/x-$1-request" \
+       "$HTTPD_URL/smart/repo.git/$1" >out 2>/dev/null &&
+       tr '\015' Q <out |
+       sed '
+               s/Q$//
+               1q
+       ' >act &&
+       echo "HTTP/1.1 $3" >exp &&
+       test_cmp exp act
+}
+
+log_div() {
+       echo >>"$HTTPD_ROOT_PATH"/access.log
+       echo "###  $1" >>"$HTTPD_ROOT_PATH"/access.log
+       echo "###" >>"$HTTPD_ROOT_PATH"/access.log
+}
+
+. "$TEST_DIRECTORY"/t556x_common
+
+cat >exp <<EOF
+
+###  refs/heads/master
+###
+GET  /smart/repo.git/refs/heads/master HTTP/1.1 404 -
+
+###  getanyfile default
+###
+GET  /smart/repo.git/HEAD HTTP/1.1 200
+GET  /smart/repo.git/info/refs HTTP/1.1 200
+GET  /smart/repo.git/objects/info/packs HTTP/1.1 200
+GET  /smart/repo.git/objects/info/alternates HTTP/1.1 200 -
+GET  /smart/repo.git/objects/info/http-alternates HTTP/1.1 200 -
+GET  /smart/repo.git/$LOOSE_URL HTTP/1.1 200
+GET  /smart/repo.git/$PACK_URL HTTP/1.1 200
+GET  /smart/repo.git/$IDX_URL HTTP/1.1 200
+
+###  no git-daemon-export-ok
+###
+GET  /smart_noexport/repo.git/HEAD HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/info/refs HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/objects/info/packs HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/objects/info/alternates HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/objects/info/http-alternates HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/$LOOSE_URL HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/$PACK_URL HTTP/1.1 404 -
+GET  /smart_noexport/repo.git/$IDX_URL HTTP/1.1 404 -
+
+###  git-daemon-export-ok
+###
+GET  /smart_noexport/repo.git/HEAD HTTP/1.1 200
+GET  /smart_noexport/repo.git/info/refs HTTP/1.1 200
+GET  /smart_noexport/repo.git/objects/info/packs HTTP/1.1 200
+GET  /smart_noexport/repo.git/objects/info/alternates HTTP/1.1 200 -
+GET  /smart_noexport/repo.git/objects/info/http-alternates HTTP/1.1 200 -
+GET  /smart_noexport/repo.git/$LOOSE_URL HTTP/1.1 200
+GET  /smart_noexport/repo.git/$PACK_URL HTTP/1.1 200
+GET  /smart_noexport/repo.git/$IDX_URL HTTP/1.1 200
+
+###  getanyfile true
+###
+GET  /smart/repo.git/HEAD HTTP/1.1 200
+GET  /smart/repo.git/info/refs HTTP/1.1 200
+GET  /smart/repo.git/objects/info/packs HTTP/1.1 200
+GET  /smart/repo.git/objects/info/alternates HTTP/1.1 200 -
+GET  /smart/repo.git/objects/info/http-alternates HTTP/1.1 200 -
+GET  /smart/repo.git/$LOOSE_URL HTTP/1.1 200
+GET  /smart/repo.git/$PACK_URL HTTP/1.1 200
+GET  /smart/repo.git/$IDX_URL HTTP/1.1 200
+
+###  getanyfile false
+###
+GET  /smart/repo.git/HEAD HTTP/1.1 403 -
+GET  /smart/repo.git/info/refs HTTP/1.1 403 -
+GET  /smart/repo.git/objects/info/packs HTTP/1.1 403 -
+GET  /smart/repo.git/objects/info/alternates HTTP/1.1 403 -
+GET  /smart/repo.git/objects/info/http-alternates HTTP/1.1 403 -
+GET  /smart/repo.git/$LOOSE_URL HTTP/1.1 403 -
+GET  /smart/repo.git/$PACK_URL HTTP/1.1 403 -
+GET  /smart/repo.git/$IDX_URL HTTP/1.1 403 -
+
+###  uploadpack default
+###
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200 -
+
+###  uploadpack true
+###
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+POST /smart/repo.git/git-upload-pack HTTP/1.1 200 -
+
+###  uploadpack false
+###
+GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 403 -
+POST /smart/repo.git/git-upload-pack HTTP/1.1 403 -
+
+###  receivepack default
+###
+GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 403 -
+POST /smart/repo.git/git-receive-pack HTTP/1.1 403 -
+
+###  receivepack true
+###
+GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
+POST /smart/repo.git/git-receive-pack HTTP/1.1 200 -
+
+###  receivepack false
+###
+GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 403 -
+POST /smart/repo.git/git-receive-pack HTTP/1.1 403 -
+EOF
+test_expect_success 'server request log matches test results' '
+       sed -e "
+               s/^.* \"//
+               s/\"//
+               s/ [1-9][0-9]*\$//
+               s/^GET /GET  /
+       " >act <"$HTTPD_ROOT_PATH"/access.log &&
+       test_cmp exp act
+'
+
+stop_httpd
+test_done
diff --git a/t/t556x_common b/t/t556x_common
new file mode 100755 (executable)
index 0000000..be024e5
--- /dev/null
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+find_file() {
+       cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       find $1 -type f |
+       sed -e 1q
+}
+
+config() {
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" config $1 $2
+}
+
+test_expect_success 'setup repository' '
+       echo content >file &&
+       git add file &&
+       git commit -m one &&
+
+       mkdir "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+        git --bare init &&
+        : >objects/info/alternates &&
+        : >objects/info/http-alternates
+       ) &&
+       git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+       git push public master:master &&
+
+       (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+        git repack -a -d
+       ) &&
+
+       echo other >file &&
+       git add file &&
+       git commit -m two &&
+       git push public master:master &&
+
+       LOOSE_URL=$(find_file objects/??) &&
+       PACK_URL=$(find_file objects/pack/*.pack) &&
+       IDX_URL=$(find_file objects/pack/*.idx)
+'
+
+get_static_files() {
+       GET HEAD "$1" &&
+       GET info/refs "$1" &&
+       GET objects/info/packs "$1" &&
+       GET objects/info/alternates "$1" &&
+       GET objects/info/http-alternates "$1" &&
+       GET $LOOSE_URL "$1" &&
+       GET $PACK_URL "$1" &&
+       GET $IDX_URL "$1"
+}
+
+SMART=smart
+export GIT_HTTP_EXPORT_ALL=1
+test_expect_success 'direct refs/heads/master not found' '
+       log_div "refs/heads/master"
+       GET refs/heads/master "404 Not Found"
+'
+test_expect_success 'static file is ok' '
+       log_div "getanyfile default"
+       get_static_files "200 OK"
+'
+SMART=smart_noexport
+unset GIT_HTTP_EXPORT_ALL
+test_expect_success 'no export by default' '
+       log_div "no git-daemon-export-ok"
+       get_static_files "404 Not Found"
+'
+test_expect_success 'export if git-daemon-export-ok' '
+       log_div "git-daemon-export-ok"
+        (cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
+        touch git-daemon-export-ok
+       ) &&
+        get_static_files "200 OK"
+'
+SMART=smart
+export GIT_HTTP_EXPORT_ALL=1
+test_expect_success 'static file if http.getanyfile true is ok' '
+       log_div "getanyfile true"
+       config http.getanyfile true &&
+       get_static_files "200 OK"
+'
+test_expect_success 'static file if http.getanyfile false fails' '
+       log_div "getanyfile false"
+       config http.getanyfile false &&
+       get_static_files "403 Forbidden"
+'
+
+test_expect_success 'http.uploadpack default enabled' '
+       log_div "uploadpack default"
+       GET info/refs?service=git-upload-pack "200 OK"  &&
+       POST git-upload-pack 0000 "200 OK"
+'
+test_expect_success 'http.uploadpack true' '
+       log_div "uploadpack true"
+       config http.uploadpack true &&
+       GET info/refs?service=git-upload-pack "200 OK" &&
+       POST git-upload-pack 0000 "200 OK"
+'
+test_expect_success 'http.uploadpack false' '
+       log_div "uploadpack false"
+       config http.uploadpack false &&
+       GET info/refs?service=git-upload-pack "403 Forbidden" &&
+       POST git-upload-pack 0000 "403 Forbidden"
+'
+
+test_expect_success 'http.receivepack default disabled' '
+       log_div "receivepack default"
+       GET info/refs?service=git-receive-pack "403 Forbidden"  &&
+       POST git-receive-pack 0000 "403 Forbidden"
+'
+test_expect_success 'http.receivepack true' '
+       log_div "receivepack true"
+       config http.receivepack true &&
+       GET info/refs?service=git-receive-pack "200 OK" &&
+       POST git-receive-pack 0000 "200 OK"
+'
+test_expect_success 'http.receivepack false' '
+       log_div "receivepack false"
+       config http.receivepack false &&
+       GET info/refs?service=git-receive-pack "403 Forbidden" &&
+       POST git-receive-pack 0000 "403 Forbidden"
+'
index 27825f5f31ca2310e08fd2555589aa878728b292..02cb02472322e8fd6c31548d30950c937e301e5f 100755 (executable)
@@ -27,7 +27,8 @@ test_expect_success 'redirected clone' '
 '
 test_expect_success 'redirected clone -v' '
 
-       git clone -v "file://$(pwd)/parent" clone-redirected-v >out 2>err &&
+       git clone --progress "file://$(pwd)/parent" clone-redirected-progress \
+               >out 2>err &&
        test -s err
 
 '
index def397c53a76dead449710eaca6333c2e1fb36aa..c51865fdbc0a6fd98cca4a4accd35b302e5fd739 100755 (executable)
@@ -423,7 +423,7 @@ test_expect_success 'skipped merge base when good and bad are siblings' '
        grep "merge base must be tested" my_bisect_log.txt &&
        grep $HASH4 my_bisect_log.txt &&
        git bisect skip > my_bisect_log.txt 2>&1 &&
-       grep "Warning" my_bisect_log.txt &&
+       grep "warning" my_bisect_log.txt &&
        grep $SIDE_HASH6 my_bisect_log.txt &&
        git bisect reset
 '
index abd14bf819f5c60fc1b9dc758c04974bd24b10a4..76c5e091b7d45bde38f34dc9726c51034ee7f9eb 100755 (executable)
@@ -8,6 +8,18 @@ test_description='git grep various.
 
 . ./test-lib.sh
 
+test_expect_success 'Check for external grep support' '
+       case "$(git grep -h 2>&1|grep ext-grep)" in
+       *"(default)"*)
+               test_set_prereq EXTGREP
+               true;;
+       *"(ignored by this build)"*)
+               true;;
+       *)
+               false;;
+       esac
+'
+
 cat >hello.c <<EOF
 #include <stdio.h>
 int main(int argc, const char **argv)
@@ -426,4 +438,16 @@ test_expect_success 'grep -Fi' '
        test_cmp expected actual
 '
 
+test_expect_success EXTGREP 'external grep is called' '
+       GIT_TRACE=2 git grep foo >/dev/null 2>actual &&
+       grep "trace: grep:.*foo" actual >/dev/null
+'
+
+test_expect_success EXTGREP 'no external grep when skip-worktree entries exist' '
+       git update-index --skip-worktree file &&
+       GIT_TRACE=2 git grep foo >/dev/null 2>actual &&
+       ! grep "trace: grep:" actual >/dev/null &&
+       git update-index --no-skip-worktree file
+'
+
 test_done
diff --git a/t/t7011-skip-worktree-reading.sh b/t/t7011-skip-worktree-reading.sh
new file mode 100755 (executable)
index 0000000..bb4066f
--- /dev/null
@@ -0,0 +1,163 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Nguyễn Thái Ngọc Duy
+#
+
+test_description='skip-worktree bit test'
+
+. ./test-lib.sh
+
+cat >expect.full <<EOF
+H 1
+H 2
+H init.t
+H sub/1
+H sub/2
+EOF
+
+cat >expect.skip <<EOF
+S 1
+H 2
+H init.t
+S sub/1
+H sub/2
+EOF
+
+NULL_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+ZERO_SHA0=0000000000000000000000000000000000000000
+setup_absent() {
+       test -f 1 && rm 1
+       git update-index --remove 1 &&
+       git update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&
+       git update-index --skip-worktree 1
+}
+
+test_absent() {
+       echo "100644 $NULL_SHA1 0       1" > expected &&
+       git ls-files --stage 1 > result &&
+       test_cmp expected result &&
+       test ! -f 1
+}
+
+setup_dirty() {
+       git update-index --force-remove 1 &&
+       echo dirty > 1 &&
+       git update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&
+       git update-index --skip-worktree 1
+}
+
+test_dirty() {
+       echo "100644 $NULL_SHA1 0       1" > expected &&
+       git ls-files --stage 1 > result &&
+       test_cmp expected result &&
+       echo dirty > expected
+       test_cmp expected 1
+}
+
+test_expect_success 'setup' '
+       test_commit init &&
+       mkdir sub &&
+       touch ./1 ./2 sub/1 sub/2 &&
+       git add 1 2 sub/1 sub/2 &&
+       git update-index --skip-worktree 1 sub/1 &&
+       git ls-files -t > result &&
+       test_cmp expect.skip result
+'
+
+test_expect_success 'update-index' '
+       setup_absent &&
+       git update-index 1 &&
+       test_absent
+'
+
+test_expect_success 'update-index' '
+       setup_dirty &&
+       git update-index 1 &&
+       test_dirty
+'
+
+test_expect_success 'update-index --remove' '
+       setup_absent &&
+       git update-index --remove 1 &&
+       test -z "$(git ls-files 1)" &&
+       test ! -f 1
+'
+
+test_expect_success 'update-index --remove' '
+       setup_dirty &&
+       git update-index --remove 1 &&
+       test -z "$(git ls-files 1)" &&
+       echo dirty > expected &&
+       test_cmp expected 1
+'
+
+test_expect_success 'ls-files --delete' '
+       setup_absent &&
+       test -z "$(git ls-files -d)"
+'
+
+test_expect_success 'ls-files --delete' '
+       setup_dirty &&
+       test -z "$(git ls-files -d)"
+'
+
+test_expect_success 'ls-files --modified' '
+       setup_absent &&
+       test -z "$(git ls-files -m)"
+'
+
+test_expect_success 'ls-files --modified' '
+       setup_dirty &&
+       test -z "$(git ls-files -m)"
+'
+
+test_expect_success 'grep with skip-worktree file' '
+       git update-index --no-skip-worktree 1 &&
+       echo test > 1 &&
+       git update-index 1 &&
+       git update-index --skip-worktree 1 &&
+       rm 1 &&
+       test "$(git grep --no-ext-grep test)" = "1:test"
+'
+
+echo ":000000 100644 $ZERO_SHA0 $NULL_SHA1 A   1" > expected
+test_expect_success 'diff-index does not examine skip-worktree absent entries' '
+       setup_absent &&
+       git diff-index HEAD -- 1 > result &&
+       test_cmp expected result
+'
+
+test_expect_success 'diff-index does not examine skip-worktree dirty entries' '
+       setup_dirty &&
+       git diff-index HEAD -- 1 > result &&
+       test_cmp expected result
+'
+
+test_expect_success 'diff-files does not examine skip-worktree absent entries' '
+       setup_absent &&
+       test -z "$(git diff-files -- one)"
+'
+
+test_expect_success 'diff-files does not examine skip-worktree dirty entries' '
+       setup_dirty &&
+       test -z "$(git diff-files -- one)"
+'
+
+test_expect_success 'git-rm succeeds on skip-worktree absent entries' '
+       setup_absent &&
+       git rm 1
+'
+
+test_expect_success 'commit on skip-worktree absent entries' '
+       git reset &&
+       setup_absent &&
+       test_must_fail git commit -m null 1
+'
+
+test_expect_success 'commit on skip-worktree dirty entries' '
+       git reset &&
+       setup_dirty &&
+       test_must_fail git commit -m null 1
+'
+
+test_done
diff --git a/t/t7012-skip-worktree-writing.sh b/t/t7012-skip-worktree-writing.sh
new file mode 100755 (executable)
index 0000000..8d8b1c0
--- /dev/null
@@ -0,0 +1,146 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Nguyễn Thái Ngọc Duy
+#
+
+test_description='test worktree writing operations when skip-worktree is used'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit init &&
+       echo modified >> init.t &&
+       touch added &&
+       git add init.t added &&
+       git commit -m "modified and added" &&
+       git tag top
+'
+
+test_expect_success 'read-tree updates worktree, absent case' '
+       git checkout -f top &&
+       git update-index --skip-worktree init.t &&
+       rm init.t &&
+       git read-tree -m -u HEAD^ &&
+       echo init > expected &&
+       test_cmp expected init.t
+'
+
+test_expect_success 'read-tree updates worktree, dirty case' '
+       git checkout -f top &&
+       git update-index --skip-worktree init.t &&
+       echo dirty >> init.t &&
+       test_must_fail git read-tree -m -u HEAD^ &&
+       grep -q dirty init.t &&
+       test "$(git ls-files -t init.t)" = "S init.t" &&
+       git update-index --no-skip-worktree init.t
+'
+
+test_expect_success 'read-tree removes worktree, absent case' '
+       git checkout -f top &&
+       git update-index --skip-worktree added &&
+       rm added &&
+       git read-tree -m -u HEAD^ &&
+       test ! -f added
+'
+
+test_expect_success 'read-tree removes worktree, dirty case' '
+       git checkout -f top &&
+       git update-index --skip-worktree added &&
+       echo dirty >> added &&
+       test_must_fail git read-tree -m -u HEAD^ &&
+       grep -q dirty added &&
+       test "$(git ls-files -t added)" = "S added" &&
+       git update-index --no-skip-worktree added
+'
+
+NULL_SHA1=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+ZERO_SHA0=0000000000000000000000000000000000000000
+setup_absent() {
+       test -f 1 && rm 1
+       git update-index --remove 1 &&
+       git update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&
+       git update-index --skip-worktree 1
+}
+
+test_absent() {
+       echo "100644 $NULL_SHA1 0       1" > expected &&
+       git ls-files --stage 1 > result &&
+       test_cmp expected result &&
+       test ! -f 1
+}
+
+setup_dirty() {
+       git update-index --force-remove 1 &&
+       echo dirty > 1 &&
+       git update-index --add --cacheinfo 100644 $NULL_SHA1 1 &&
+       git update-index --skip-worktree 1
+}
+
+test_dirty() {
+       echo "100644 $NULL_SHA1 0       1" > expected &&
+       git ls-files --stage 1 > result &&
+       test_cmp expected result &&
+       echo dirty > expected
+       test_cmp expected 1
+}
+
+cat >expected <<EOF
+S 1
+H 2
+H init.t
+S sub/1
+H sub/2
+EOF
+
+test_expect_success 'index setup' '
+       git checkout -f init &&
+       mkdir sub &&
+       touch ./1 ./2 sub/1 sub/2 &&
+       git add 1 2 sub/1 sub/2 &&
+       git update-index --skip-worktree 1 sub/1 &&
+       git ls-files -t > result &&
+       test_cmp expected result
+'
+
+test_expect_success 'git-add ignores worktree content' '
+       setup_absent &&
+       git add 1 &&
+       test_absent
+'
+
+test_expect_success 'git-add ignores worktree content' '
+       setup_dirty &&
+       git add 1 &&
+       test_dirty
+'
+
+test_expect_success 'git-rm fails if worktree is dirty' '
+       setup_dirty &&
+       test_must_fail git rm 1 &&
+       test_dirty
+'
+
+cat >expected <<EOF
+Would remove expected
+Would remove result
+EOF
+test_expect_success 'git-clean, absent case' '
+       setup_absent &&
+       git clean -n > result &&
+       test_cmp expected result
+'
+
+test_expect_success 'git-clean, dirty case' '
+       setup_dirty &&
+       git clean -n > result &&
+       test_cmp expected result
+'
+
+test_expect_failure 'git-apply adds file' false
+test_expect_failure 'git-apply updates file' false
+test_expect_failure 'git-apply removes file' false
+test_expect_failure 'git-mv to skip-worktree' false
+test_expect_failure 'git-mv from skip-worktree' false
+test_expect_failure 'git-checkout' false
+
+test_done
index e85ff02c3e636567a80586e92fdba1c5382fc995..b8cf2603a195af406d3606712e45fd1195c1588f 100755 (executable)
@@ -139,19 +139,19 @@ test_expect_success \
 test_expect_success \
        'resetting to HEAD with no changes should succeed and do nothing' '
        git reset --hard &&
-               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
        git reset --hard HEAD &&
-               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
        git reset --soft &&
-               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
        git reset --soft HEAD &&
-               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
        git reset --mixed &&
-               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
        git reset --mixed HEAD &&
-               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
        git reset &&
-               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
+               check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc &&
        git reset HEAD &&
                check_changes 3ec39651e7f44ea531a5de18a9fa791c0fd370fc
 '
index 68041df5f49e046c2b7ccd1f3a1c71071498da39..afb55b3a463f79be83c0e3cc4a8aff8a0c6676be 100755 (executable)
@@ -29,6 +29,12 @@ test_expect_success 'soft reset is ok' '
        (cd .git && git reset --soft)
 '
 
+test_expect_success 'hard reset works with GIT_WORK_TREE' '
+       mkdir worktree &&
+       GIT_WORK_TREE=$PWD/worktree GIT_DIR=$PWD/.git git reset --hard &&
+       test_cmp file worktree/file
+'
+
 test_expect_success 'setup bare' '
        git clone --bare . bare.git &&
        cd bare.git
diff --git a/t/t7110-reset-merge.sh b/t/t7110-reset-merge.sh
new file mode 100755 (executable)
index 0000000..8704d00
--- /dev/null
@@ -0,0 +1,183 @@
+#!/bin/sh
+#
+# Copyright (c) 2009 Christian Couder
+#
+
+test_description='Tests for "git reset --merge"'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+    for i in 1 2 3; do echo line $i; done >file1 &&
+    cat file1 >file2 &&
+    git add file1 file2 &&
+    test_tick &&
+    git commit -m "Initial commit" &&
+    git tag initial &&
+    echo line 4 >>file1 &&
+    cat file1 >file2 &&
+    test_tick &&
+    git commit -m "add line 4 to file1" file1 &&
+    git tag second
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     C       C     C    D     --merge  D       D     D
+# file2:     C       D     D    D     --merge  C       D     D
+test_expect_success 'reset --merge is ok with changes in file it does not touch' '
+    git reset --merge HEAD^ &&
+    ! grep 4 file1 &&
+    grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+    test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --merge is ok when switching back' '
+    git reset --merge second &&
+    grep 4 file1 &&
+    grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+    test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     B       B     C    D     --merge  D       D     D
+# file2:     C       D     D    D     --merge  C       D     D
+test_expect_success 'reset --merge discards changes added to index (1)' '
+    git reset --hard second &&
+    cat file1 >file2 &&
+    echo "line 5" >> file1 &&
+    git add file1 &&
+    git reset --merge HEAD^ &&
+    ! grep 4 file1 &&
+    ! grep 5 file1 &&
+    grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+    test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --merge is ok again when switching back (1)' '
+    git reset --hard initial &&
+    echo "line 5" >> file2 &&
+    git add file2 &&
+    git reset --merge second &&
+    ! grep 4 file2 &&
+    ! grep 5 file1 &&
+    grep 4 file1 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+    test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     C       C     C    D     --merge  D       D     D
+# file2:     C       C     D    D     --merge  D       D     D
+test_expect_success 'reset --merge discards changes added to index (2)' '
+    git reset --hard second &&
+    echo "line 4" >> file2 &&
+    git add file2 &&
+    git reset --merge HEAD^ &&
+    ! grep 4 file2 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse initial)" &&
+    test -z "$(git diff)" &&
+    test -z "$(git diff --cached)"
+'
+
+test_expect_success 'reset --merge is ok again when switching back (2)' '
+    git reset --hard initial &&
+    git reset --merge second &&
+    ! grep 4 file2 &&
+    grep 4 file1 &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+    test -z "$(git diff --cached)"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     A       B     B    C     --merge  (disallowed)
+test_expect_success 'reset --merge fails with changes in file it touches' '
+    git reset --hard second &&
+    echo "line 5" >> file1 &&
+    test_tick &&
+    git commit -m "add line 5" file1 &&
+    sed -e "s/line 1/changed line 1/" <file1 >file3 &&
+    mv file3 file1 &&
+    test_must_fail git reset --merge HEAD^ 2>err.log &&
+    grep file1 err.log | grep "not uptodate"
+'
+
+test_expect_success 'setup 3 different branches' '
+    git reset --hard second &&
+    git branch branch1 &&
+    git branch branch2 &&
+    git branch branch3 &&
+    git checkout branch1 &&
+    echo "line 5 in branch1" >> file1 &&
+    test_tick &&
+    git commit -a -m "change in branch1" &&
+    git checkout branch2 &&
+    echo "line 5 in branch2" >> file1 &&
+    test_tick &&
+    git commit -a -m "change in branch2" &&
+    git tag third &&
+    git checkout branch3 &&
+    echo a new file >file3 &&
+    rm -f file1 &&
+    git add file3 &&
+    test_tick &&
+    git commit -a -m "change in branch3"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     X       U     B    C     --merge  C       C     C
+test_expect_success '"reset --merge HEAD^" is ok with pending merge' '
+    git checkout third &&
+    test_must_fail git merge branch1 &&
+    git reset --merge HEAD^ &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse second)" &&
+    test -z "$(git diff --cached)" &&
+    test -z "$(git diff)"
+'
+
+# The next test will test the following:
+#
+#           working index HEAD target         working index HEAD
+#           ----------------------------------------------------
+# file1:     X       U     B    B     --merge  B       B     B
+test_expect_success '"reset --merge HEAD" is ok with pending merge' '
+    git reset --hard third &&
+    test_must_fail git merge branch1 &&
+    git reset --merge HEAD &&
+    test "$(git rev-parse HEAD)" = "$(git rev-parse third)" &&
+    test -z "$(git diff --cached)" &&
+    test -z "$(git diff)"
+'
+
+test_expect_success '--merge with added/deleted' '
+    git reset --hard third &&
+    rm -f file2 &&
+    test_must_fail git merge branch3 &&
+    ! test -f file2 &&
+    test -f file3 &&
+    git diff --exit-code file3 &&
+    git diff --exit-code branch3 file3 &&
+    git reset --merge HEAD &&
+    ! test -f file3 &&
+    ! test -f file2 &&
+    git diff --exit-code --cached
+'
+
+test_done
diff --git a/t/t7111-reset-table.sh b/t/t7111-reset-table.sh
new file mode 100755 (executable)
index 0000000..de896c9
--- /dev/null
@@ -0,0 +1,113 @@
+#!/bin/sh
+#
+# Copyright (c) 2010 Christian Couder
+#
+
+test_description='Tests to check that "reset" options follow a known table'
+
+. ./test-lib.sh
+
+
+test_expect_success 'creating initial commits' '
+    test_commit E file1 &&
+    test_commit D file1 &&
+    test_commit C file1
+'
+
+while read W1 I1 H1 T opt W2 I2 H2
+do
+    test_expect_success "check: $W1 $I1 $H1 $T --$opt $W2 $I2 $H2" '
+       git reset --hard C &&
+       if test "$I1" != "$H1"
+       then
+           echo "$I1" >file1 &&
+           git add file1
+       fi &&
+       if test "$W1" != "$I1"
+       then
+           echo "$W1" >file1
+       fi &&
+       if test "$W2" != "XXXXX"
+       then
+           git reset --$opt $T &&
+           test "$(cat file1)" = "$W2" &&
+           git checkout-index -f -- file1 &&
+           test "$(cat file1)" = "$I2" &&
+           git checkout -f HEAD -- file1 &&
+           test "$(cat file1)" = "$H2"
+       else
+           test_must_fail git reset --$opt $T
+       fi
+    '
+done <<\EOF
+A B C D soft   A B D
+A B C D mixed  A D D
+A B C D hard   D D D
+A B C D merge  XXXXX
+A B C C soft   A B C
+A B C C mixed  A C C
+A B C C hard   C C C
+A B C C merge  XXXXX
+B B C D soft   B B D
+B B C D mixed  B D D
+B B C D hard   D D D
+B B C D merge  D D D
+B B C C soft   B B C
+B B C C mixed  B C C
+B B C C hard   C C C
+B B C C merge  C C C
+B C C D soft   B C D
+B C C D mixed  B D D
+B C C D hard   D D D
+B C C D merge  XXXXX
+B C C C soft   B C C
+B C C C mixed  B C C
+B C C C hard   C C C
+B C C C merge  B C C
+EOF
+
+test_expect_success 'setting up branches to test with unmerged entries' '
+    git reset --hard C &&
+    git branch branch1 &&
+    git branch branch2 &&
+    git checkout branch1 &&
+    test_commit B1 file1 &&
+    git checkout branch2 &&
+    test_commit B file1
+'
+
+while read W1 I1 H1 T opt W2 I2 H2
+do
+    test_expect_success "check: $W1 $I1 $H1 $T --$opt $W2 $I2 $H2" '
+       git reset --hard B &&
+       test_must_fail git merge branch1 &&
+       cat file1 >X_file1 &&
+       if test "$W2" != "XXXXX"
+       then
+           git reset --$opt $T &&
+           if test "$W2" = "X"
+           then
+               test_cmp file1 X_file1
+           else
+               test "$(cat file1)" = "$W2"
+           fi &&
+           git checkout-index -f -- file1 &&
+           test "$(cat file1)" = "$I2" &&
+           git checkout -f HEAD -- file1 &&
+           test "$(cat file1)" = "$H2"
+       else
+           test_must_fail git reset --$opt $T
+       fi
+    '
+done <<\EOF
+X U B C soft   XXXXX
+X U B C mixed  X C C
+X U B C hard   C C C
+X U B C merge  C C C
+X U B B soft   XXXXX
+X U B B mixed  X B B
+X U B B hard   B B B
+X U B B merge  B B B
+EOF
+
+test_done
index ebfd34df36068f8808406a98d371731fb85012c4..6442f710be8bcaea11931044d52deb5b75d8f7e0 100755 (executable)
@@ -542,4 +542,61 @@ test_expect_success 'switch out of non-branch' '
        ! grep "^Previous HEAD" error.log
 '
 
+(
+ echo "#!$SHELL_PATH"
+ cat <<\EOF
+O=$1 A=$2 B=$3
+cat "$A" >.tmp
+exec >"$A"
+echo '<<<<<<< filfre-theirs'
+cat "$B"
+echo '||||||| filfre-common'
+cat "$O"
+echo '======='
+cat ".tmp"
+echo '>>>>>>> filfre-ours'
+rm -f .tmp
+exit 1
+EOF
+) >filfre.sh
+chmod +x filfre.sh
+
+test_expect_success 'custom merge driver with checkout -m' '
+       git reset --hard &&
+
+       git config merge.filfre.driver "./filfre.sh %O %A %B" &&
+       git config merge.filfre.name "Feel-free merge driver" &&
+       git config merge.filfre.recursive binary &&
+       echo "arm merge=filfre" >.gitattributes &&
+
+       git checkout -b left &&
+       echo neutral >arm &&
+       git add arm .gitattributes &&
+       test_tick &&
+       git commit -m neutral &&
+       git branch right &&
+
+       echo left >arm &&
+       test_tick &&
+       git commit -a -m left &&
+       git checkout right &&
+
+       echo right >arm &&
+       test_tick &&
+       git commit -a -m right &&
+
+       test_must_fail git merge left &&
+       (
+               for t in filfre-common left right
+               do
+                       grep $t arm || exit 1
+               done
+               exit 0
+       ) &&
+
+       mv arm expect &&
+       git checkout -m arm &&
+       test_cmp expect arm
+'
+
 test_done
index 118c6ebb182b5cd4700e533d6a951b31529149af..7d8ed68befed0e85ad85f9f933a2b887125e38e8 100755 (executable)
@@ -22,6 +22,25 @@ test_expect_success 'setup' '
 
 '
 
+test_expect_success 'git clean with skip-worktree .gitignore' '
+       git update-index --skip-worktree .gitignore &&
+       rm .gitignore &&
+       mkdir -p build docs &&
+       touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
+       git clean &&
+       test -f Makefile &&
+       test -f README &&
+       test -f src/part1.c &&
+       test -f src/part2.c &&
+       test ! -f a.out &&
+       test ! -f src/part3.c &&
+       test -f docs/manual.txt &&
+       test -f obj.o &&
+       test -f build/lib.so &&
+       git update-index --no-skip-worktree .gitignore &&
+       git checkout .gitignore
+'
+
 test_expect_success 'git clean' '
 
        mkdir -p build docs &&
index a0cc99ab9f5b262851a1075193f5529b5582fd0a..1a4dc5f89353df7d7bda4bea539ee5bd7a3b9bae 100755 (executable)
@@ -299,6 +299,15 @@ test_expect_success 'ls-files gracefully handles trailing slash' '
 
 '
 
+test_expect_success 'moving to a commit without submodule does not leave empty dir' '
+       rm -rf init &&
+       mkdir init &&
+       git reset --hard &&
+       git checkout initial &&
+       test ! -d init &&
+       git checkout second
+'
+
 test_expect_success 'submodule <invalid-path> warns' '
 
        git submodule no-such-submodule 2> output.err &&
index a603f6d21a61df197256ea91bfb38c1f4e45c5e8..a5297012410271f64398124da6e4c7dd8eec3154 100755 (executable)
@@ -211,6 +211,21 @@ test_expect_success 'amend commit to fix author' '
 
 '
 
+test_expect_success 'amend commit to fix date' '
+
+       test_tick &&
+       newtick=$GIT_AUTHOR_DATE &&
+       git reset --hard &&
+       git cat-file -p HEAD |
+       sed -e "s/author.*/author $author $newtick/" \
+               -e "s/^\(committer.*> \).*$/\1$GIT_COMMITTER_DATE/" > \
+               expected &&
+       git commit --amend --date="$newtick" &&
+       git cat-file -p HEAD > current &&
+       test_cmp expected current
+
+'
+
 test_expect_success 'sign off (1)' '
 
        echo 1 >positive &&
index fe94552296bb98ef78dbc51e8b1f7d4a665cf0a4..844fb43c6db1ae4e9b8a3cda6156af359e9f639e 100755 (executable)
@@ -267,4 +267,113 @@ test_expect_success 'A single-liner subject with a token plus colon is not a foo
 
 '
 
+cat >.git/FAKE_EDITOR <<EOF
+#!$SHELL_PATH
+mv "\$1" "\$1.orig"
+(
+       echo message
+       cat "\$1.orig"
+) >"\$1"
+EOF
+
+echo '## Custom template' >template
+
+clear_config () {
+       (
+               git config --unset-all "$1"
+               case $? in
+               0|5)    exit 0 ;;
+               *)      exit 1 ;;
+               esac
+       )
+}
+
+try_commit () {
+       git reset --hard &&
+       echo >>negative &&
+       GIT_EDITOR=.git/FAKE_EDITOR git commit -a $* $use_template &&
+       case "$use_template" in
+       '')
+               ! grep "^## Custom template" .git/COMMIT_EDITMSG ;;
+       *)
+               grep "^## Custom template" .git/COMMIT_EDITMSG ;;
+       esac
+}
+
+try_commit_status_combo () {
+
+       test_expect_success 'commit' '
+               clear_config commit.status &&
+               try_commit "" &&
+               grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+       '
+
+       test_expect_success 'commit' '
+               clear_config commit.status &&
+               try_commit "" &&
+               grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+       '
+
+       test_expect_success 'commit --status' '
+               clear_config commit.status &&
+               try_commit --status &&
+               grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+       '
+
+       test_expect_success 'commit --no-status' '
+               clear_config commit.status &&
+               try_commit --no-status
+               ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+       '
+
+       test_expect_success 'commit with commit.status = yes' '
+               clear_config commit.status &&
+               git config commit.status yes &&
+               try_commit "" &&
+               grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+       '
+
+       test_expect_success 'commit with commit.status = no' '
+               clear_config commit.status &&
+               git config commit.status no &&
+               try_commit "" &&
+               ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+       '
+
+       test_expect_success 'commit --status with commit.status = yes' '
+               clear_config commit.status &&
+               git config commit.status yes &&
+               try_commit --status &&
+               grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+       '
+
+       test_expect_success 'commit --no-status with commit.status = yes' '
+               clear_config commit.status &&
+               git config commit.status yes &&
+               try_commit --no-status &&
+               ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+       '
+
+       test_expect_success 'commit --status with commit.status = no' '
+               clear_config commit.status &&
+               git config commit.status no &&
+               try_commit --status &&
+               grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+       '
+
+       test_expect_success 'commit --no-status with commit.status = no' '
+               clear_config commit.status &&
+               git config commit.status no &&
+               try_commit --no-status &&
+               ! grep "^# Changes to be committed:" .git/COMMIT_EDITMSG
+       '
+
+}
+
+try_commit_status_combo
+
+use_template="-t template"
+
+try_commit_status_combo
+
 test_done
index 01e5415e943f3e16154f2f4d999f85a9d8b6ae49..2746169514f9bd1629f05eed8f9ff2fcfdba8cb5 100755 (executable)
@@ -49,4 +49,55 @@ test_expect_success 'merge c1 with c2, c3, c4, ... c29' '
        done
 '
 
+cat >expected <<\EOF
+Trying simple merge with c2
+Trying simple merge with c3
+Trying simple merge with c4
+Merge made by octopus.
+ c2.c |    1 +
+ c3.c |    1 +
+ c4.c |    1 +
+ 3 files changed, 3 insertions(+), 0 deletions(-)
+ create mode 100644 c2.c
+ create mode 100644 c3.c
+ create mode 100644 c4.c
+EOF
+
+test_expect_success 'merge output uses pretty names' '
+       git reset --hard c1 &&
+       git merge c2 c3 c4 >actual &&
+       test_cmp actual expected
+'
+
+cat >expected <<\EOF
+Already up-to-date with c4
+Trying simple merge with c5
+Merge made by octopus.
+ c5.c |    1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+ create mode 100644 c5.c
+EOF
+
+test_expect_success 'merge up-to-date output uses pretty names' '
+       git merge c4 c5 >actual &&
+       test_cmp actual expected
+'
+
+cat >expected <<\EOF
+Fast-forwarding to: c1
+Trying simple merge with c2
+Merge made by octopus.
+ c1.c |    1 +
+ c2.c |    1 +
+ 2 files changed, 2 insertions(+), 0 deletions(-)
+ create mode 100644 c1.c
+ create mode 100644 c2.c
+EOF
+
+test_expect_success 'merge fast-forward output uses pretty names' '
+       git reset --hard c0 &&
+       git merge c1 c2 >actual &&
+       test_cmp actual expected
+'
+
 test_done
index b49815d10806a57461bb98ffd89031680f84715a..a1b8c2bb93374ba326c00f3f87a52dd550f90b2e 100755 (executable)
@@ -1254,4 +1254,156 @@ test_expect_success \
        'Q: verify note for third commit' \
        'git cat-file blob refs/notes/foobar:$commit3 >actual && test_cmp expect actual'
 
+###
+### series R (feature and option)
+###
+
+cat >input <<EOF
+feature no-such-feature-exists
+EOF
+
+test_expect_success 'R: abort on unsupported feature' '
+       test_must_fail git fast-import <input
+'
+
+cat >input <<EOF
+feature date-format=now
+EOF
+
+test_expect_success 'R: supported feature is accepted' '
+       git fast-import <input
+'
+
+cat >input << EOF
+blob
+data 3
+hi
+feature date-format=now
+EOF
+
+test_expect_success 'R: abort on receiving feature after data command' '
+       test_must_fail git fast-import <input
+'
+
+cat >input << EOF
+feature import-marks=git.marks
+feature import-marks=git2.marks
+EOF
+
+test_expect_success 'R: only one import-marks feature allowed per stream' '
+       test_must_fail git fast-import <input
+'
+
+cat >input << EOF
+feature export-marks=git.marks
+blob
+mark :1
+data 3
+hi
+
+EOF
+
+test_expect_success \
+    'R: export-marks feature results in a marks file being created' \
+    'cat input | git fast-import &&
+    grep :1 git.marks'
+
+test_expect_success \
+    'R: export-marks options can be overriden by commandline options' \
+    'cat input | git fast-import --export-marks=other.marks &&
+    grep :1 other.marks'
+
+cat >input << EOF
+feature import-marks=marks.out
+feature export-marks=marks.new
+EOF
+
+test_expect_success \
+    'R: import to output marks works without any content' \
+    'cat input | git fast-import &&
+    test_cmp marks.out marks.new'
+
+cat >input <<EOF
+feature import-marks=nonexistant.marks
+feature export-marks=marks.new
+EOF
+
+test_expect_success \
+    'R: import marks prefers commandline marks file over the stream' \
+    'cat input | git fast-import --import-marks=marks.out &&
+    test_cmp marks.out marks.new'
+
+
+cat >input <<EOF
+feature import-marks=nonexistant.marks
+feature export-marks=combined.marks
+EOF
+
+test_expect_success 'R: multiple --import-marks= should be honoured' '
+    head -n2 marks.out > one.marks &&
+    tail -n +3 marks.out > two.marks &&
+    git fast-import --import-marks=one.marks --import-marks=two.marks <input &&
+    test_cmp marks.out combined.marks
+'
+
+cat >input <<EOF
+feature relative-marks
+feature import-marks=relative.in
+feature export-marks=relative.out
+EOF
+
+test_expect_success 'R: feature relative-marks should be honoured' '
+    mkdir -p .git/info/fast-import/ &&
+    cp marks.new .git/info/fast-import/relative.in &&
+    git fast-import <input &&
+    test_cmp marks.new .git/info/fast-import/relative.out
+'
+
+cat >input <<EOF
+feature relative-marks
+feature import-marks=relative.in
+feature no-relative-marks
+feature export-marks=non-relative.out
+EOF
+
+test_expect_success 'R: feature no-relative-marks should be honoured' '
+    git fast-import <input &&
+    test_cmp marks.new non-relative.out
+'
+
+cat >input << EOF
+option git quiet
+blob
+data 3
+hi
+
+EOF
+
+touch empty
+
+test_expect_success 'R: quiet option results in no stats being output' '
+    cat input | git fast-import 2> output &&
+    test_cmp empty output
+'
+
+cat >input <<EOF
+option git non-existing-option
+EOF
+
+test_expect_success 'R: die on unknown option' '
+    test_must_fail git fast-import <input
+'
+
+test_expect_success 'R: unknown commandline options are rejected' '\
+    test_must_fail git fast-import --non-existing-option < /dev/null
+'
+
+cat >input <<EOF
+option non-existing-vcs non-existing-option
+EOF
+
+test_expect_success 'R: ignore non-git options' '
+    git fast-import <input
+'
+
 test_done
index 142f36f2e919929520d240e56b65f2b69e4b4982..c1476f9a2391396a201a919025963b82f7e14217 100644 (file)
@@ -105,6 +105,8 @@ do
                verbose=t; shift ;;
        -q|--q|--qu|--qui|--quie|--quiet)
                quiet=t; shift ;;
+       --with-dashes)
+               with_dashes=t; shift ;;
        --no-color)
                color=; shift ;;
        --no-python)
@@ -562,19 +564,8 @@ test_done () {
 # Test the binaries we have just built.  The tests are kept in
 # t/ subdirectory and are run in 'trash directory' subdirectory.
 TEST_DIRECTORY=$(pwd)
-if test -z "$valgrind"
+if test -n "$valgrind"
 then
-       if test -z "$GIT_TEST_INSTALLED"
-       then
-               PATH=$TEST_DIRECTORY/..:$PATH
-               GIT_EXEC_PATH=$TEST_DIRECTORY/..
-       else
-               GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
-               error "Cannot run git from $GIT_TEST_INSTALLED."
-               PATH=$GIT_TEST_INSTALLED:$TEST_DIRECTORY/..:$PATH
-               GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
-       fi
-else
        make_symlink () {
                test -h "$2" &&
                test "$1" = "$(readlink "$2")" || {
@@ -636,6 +627,24 @@ else
        PATH=$GIT_VALGRIND/bin:$PATH
        GIT_EXEC_PATH=$GIT_VALGRIND/bin
        export GIT_VALGRIND
+elif test -n "$GIT_TEST_INSTALLED" ; then
+       GIT_EXEC_PATH=$($GIT_TEST_INSTALLED/git --exec-path)  ||
+       error "Cannot run git from $GIT_TEST_INSTALLED."
+       PATH=$GIT_TEST_INSTALLED:$TEST_DIRECTORY/..:$PATH
+       GIT_EXEC_PATH=${GIT_TEST_EXEC_PATH:-$GIT_EXEC_PATH}
+else # normal case, use ../bin-wrappers only unless $with_dashes:
+       git_bin_dir="$TEST_DIRECTORY/../bin-wrappers"
+       if ! test -x "$git_bin_dir/git" ; then
+               if test -z "$with_dashes" ; then
+                       say "$git_bin_dir/git is not executable; using GIT_EXEC_PATH"
+               fi
+               with_dashes=t
+       fi
+       PATH="$git_bin_dir:$PATH"
+       GIT_EXEC_PATH=$TEST_DIRECTORY/..
+       if test -n "$with_dashes" ; then
+               PATH="$TEST_DIRECTORY/..:$PATH"
+       fi
 fi
 GIT_TEMPLATE_DIR=$(pwd)/../templates/blt
 unset GIT_CONFIG
diff --git a/test-index-version.c b/test-index-version.c
new file mode 100644 (file)
index 0000000..bfaad9e
--- /dev/null
@@ -0,0 +1,14 @@
+#include "cache.h"
+
+int main(int argc, const char **argv)
+{
+       struct cache_header hdr;
+       int version;
+
+       memset(&hdr,0,sizeof(hdr));
+       if (read(0, &hdr, sizeof(hdr)) != sizeof(hdr))
+               return 0;
+       version = ntohl(hdr.hdr_version);
+       printf("%d\n", version);
+       return 0;
+}
index 8c0b575f322c56c701448adc7acc976575e1ad98..fdf22562201d12d1321baae9157a0ca70053a7cb 100644 (file)
@@ -8,6 +8,8 @@
 #include "quote.h"
 #include "remote.h"
 
+static int debug;
+
 struct helper_data
 {
        const char *name;
@@ -16,12 +18,81 @@ struct helper_data
        unsigned fetch : 1,
                import : 1,
                option : 1,
-               push : 1;
+               push : 1,
+               connect : 1,
+               no_disconnect_req : 1;
        /* These go from remote name (as in "list") to private name */
        struct refspec *refspecs;
        int refspec_nr;
+       /* Transport options for fetch-pack/send-pack (should one of
+        * those be invoked).
+        */
+       struct git_transport_options transport_options;
 };
 
+static void sendline(struct helper_data *helper, struct strbuf *buffer)
+{
+       if (debug)
+               fprintf(stderr, "Debug: Remote helper: -> %s", buffer->buf);
+       if (write_in_full(helper->helper->in, buffer->buf, buffer->len)
+               != buffer->len)
+               die_errno("Full write to remote helper failed");
+}
+
+static int recvline_fh(FILE *helper, struct strbuf *buffer)
+{
+       strbuf_reset(buffer);
+       if (debug)
+               fprintf(stderr, "Debug: Remote helper: Waiting...\n");
+       if (strbuf_getline(buffer, helper, '\n') == EOF) {
+               if (debug)
+                       fprintf(stderr, "Debug: Remote helper quit.\n");
+               exit(128);
+       }
+
+       if (debug)
+               fprintf(stderr, "Debug: Remote helper: <- %s\n", buffer->buf);
+       return 0;
+}
+
+static int recvline(struct helper_data *helper, struct strbuf *buffer)
+{
+       return recvline_fh(helper->out, buffer);
+}
+
+static void xchgline(struct helper_data *helper, struct strbuf *buffer)
+{
+       sendline(helper, buffer);
+       recvline(helper, buffer);
+}
+
+static void write_constant(int fd, const char *str)
+{
+       if (debug)
+               fprintf(stderr, "Debug: Remote helper: -> %s", str);
+       if (write_in_full(fd, str, strlen(str)) != strlen(str))
+               die_errno("Full write to remote helper failed");
+}
+
+const char *remove_ext_force(const char *url)
+{
+       if (url) {
+               const char *colon = strchr(url, ':');
+               if (colon && colon[1] == ':')
+                       return colon + 2;
+       }
+       return url;
+}
+
+static void do_take_over(struct transport *transport)
+{
+       struct helper_data *data;
+       data = (struct helper_data *)transport->data;
+       transport_take_over(transport, data->helper);
+       fclose(data->out);
+       free(data);
+}
+
 static struct child_process *get_helper(struct transport *transport)
 {
        struct helper_data *data = transport->data;
@@ -30,6 +101,7 @@ static struct child_process *get_helper(struct transport *transport)
        const char **refspecs = NULL;
        int refspec_nr = 0;
        int refspec_alloc = 0;
+       int duped;
 
        if (data->helper)
                return data->helper;
@@ -42,34 +114,60 @@ static struct child_process *get_helper(struct transport *transport)
        strbuf_addf(&buf, "remote-%s", data->name);
        helper->argv[0] = strbuf_detach(&buf, NULL);
        helper->argv[1] = transport->remote->name;
-       helper->argv[2] = transport->url;
+       helper->argv[2] = remove_ext_force(transport->url);
        helper->git_cmd = 1;
        if (start_command(helper))
                die("Unable to run helper: git %s", helper->argv[0]);
        data->helper = helper;
+       data->no_disconnect_req = 0;
+
+       /*
+        * Open the output as FILE* so strbuf_getline() can be used.
+        * Do this with duped fd because fclose() will close the fd,
+        * and stuff like taking over will require the fd to remain.
+        */
+       duped = dup(helper->out);
+       if (duped < 0)
+               die_errno("Can't dup helper output fd");
+       data->out = xfdopen(duped, "r");
 
-       write_str_in_full(helper->in, "capabilities\n");
+       write_constant(helper->in, "capabilities\n");
 
-       data->out = xfdopen(helper->out, "r");
        while (1) {
-               if (strbuf_getline(&buf, data->out, '\n') == EOF)
-                       exit(128); /* child died, message supplied already */
+               const char *capname;
+               int mandatory = 0;
+               recvline(data, &buf);
 
                if (!*buf.buf)
                        break;
-               if (!strcmp(buf.buf, "fetch"))
+
+               if (*buf.buf == '*') {
+                       capname = buf.buf + 1;
+                       mandatory = 1;
+               } else
+                       capname = buf.buf;
+
+               if (debug)
+                       fprintf(stderr, "Debug: Got cap %s\n", capname);
+               if (!strcmp(capname, "fetch"))
                        data->fetch = 1;
-               if (!strcmp(buf.buf, "option"))
+               else if (!strcmp(capname, "option"))
                        data->option = 1;
-               if (!strcmp(buf.buf, "push"))
+               else if (!strcmp(capname, "push"))
                        data->push = 1;
-               if (!strcmp(buf.buf, "import"))
+               else if (!strcmp(capname, "import"))
                        data->import = 1;
-               if (!data->refspecs && !prefixcmp(buf.buf, "refspec ")) {
+               else if (!data->refspecs && !prefixcmp(capname, "refspec ")) {
                        ALLOC_GROW(refspecs,
                                   refspec_nr + 1,
                                   refspec_alloc);
                        refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
+               } else if (!strcmp(capname, "connect")) {
+                       data->connect = 1;
+               } else if (mandatory) {
+                       die("Unknown madatory capability %s. This remote "
+                           "helper probably needs newer version of Git.\n",
+                           capname);
                }
        }
        if (refspecs) {
@@ -82,15 +180,25 @@ static struct child_process *get_helper(struct transport *transport)
                free(refspecs);
        }
        strbuf_release(&buf);
+       if (debug)
+               fprintf(stderr, "Debug: Capabilities complete.\n");
        return data->helper;
 }
 
 static int disconnect_helper(struct transport *transport)
 {
        struct helper_data *data = transport->data;
+       struct strbuf buf = STRBUF_INIT;
+
        if (data->helper) {
-               write_str_in_full(data->helper->in, "\n");
+               if (debug)
+                       fprintf(stderr, "Debug: Disconnecting.\n");
+               if (!data->no_disconnect_req) {
+                       strbuf_addf(&buf, "\n");
+                       sendline(data, &buf);
+               }
                close(data->helper->in);
+               close(data->helper->out);
                fclose(data->out);
                finish_command(data->helper);
                free((char *)data->helper->argv[0]);
@@ -117,10 +225,11 @@ static int set_helper_option(struct transport *transport,
                          const char *name, const char *value)
 {
        struct helper_data *data = transport->data;
-       struct child_process *helper = get_helper(transport);
        struct strbuf buf = STRBUF_INIT;
        int i, ret, is_bool = 0;
 
+       get_helper(transport);
+
        if (!data->option)
                return 1;
 
@@ -143,12 +252,7 @@ static int set_helper_option(struct transport *transport,
                quote_c_style(value, &buf, NULL, 0);
        strbuf_addch(&buf, '\n');
 
-       if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
-               die_errno("cannot send option to %s", data->name);
-
-       strbuf_reset(&buf);
-       if (strbuf_getline(&buf, data->out, '\n') == EOF)
-               exit(128); /* child died, message supplied already */
+       xchgline(data, &buf);
 
        if (!strcmp(buf.buf, "ok"))
                ret = 0;
@@ -169,7 +273,7 @@ static void standard_options(struct transport *t)
        char buf[16];
        int n;
        int v = t->verbose;
-       int no_progress = v < 0 || (!t->progress && !isatty(1));
+       int no_progress = v < 0 || (!t->progress && !isatty(2));
 
        set_helper_option(t, "progress", !no_progress ? "true" : "false");
 
@@ -208,13 +312,10 @@ static int fetch_with_fetch(struct transport *transport,
        }
 
        strbuf_addch(&buf, '\n');
-       if (write_in_full(data->helper->in, buf.buf, buf.len) != buf.len)
-               die_errno("cannot send fetch to %s", data->name);
+       sendline(data, &buf);
 
        while (1) {
-               strbuf_reset(&buf);
-               if (strbuf_getline(&buf, data->out, '\n') == EOF)
-                       exit(128); /* child died, message supplied already */
+               recvline(data, &buf);
 
                if (!prefixcmp(buf.buf, "lock ")) {
                        const char *name = buf.buf + 5;
@@ -249,12 +350,13 @@ static int fetch_with_import(struct transport *transport,
                             int nr_heads, struct ref **to_fetch)
 {
        struct child_process fastimport;
-       struct child_process *helper = get_helper(transport);
        struct helper_data *data = transport->data;
        int i;
        struct ref *posn;
        struct strbuf buf = STRBUF_INIT;
 
+       get_helper(transport);
+
        if (get_importer(transport, &fastimport))
                die("Couldn't run fast-import");
 
@@ -264,7 +366,7 @@ static int fetch_with_import(struct transport *transport,
                        continue;
 
                strbuf_addf(&buf, "import %s\n", posn->name);
-               write_in_full(helper->in, buf.buf, buf.len);
+               sendline(data, &buf);
                strbuf_reset(&buf);
        }
        disconnect_helper(transport);
@@ -288,12 +390,112 @@ static int fetch_with_import(struct transport *transport,
        return 0;
 }
 
+static int process_connect_service(struct transport *transport,
+                                  const char *name, const char *exec)
+{
+       struct helper_data *data = transport->data;
+       struct strbuf cmdbuf = STRBUF_INIT;
+       struct child_process *helper;
+       int r, duped, ret = 0;
+       FILE *input;
+
+       helper = get_helper(transport);
+
+       /*
+        * Yes, dup the pipe another time, as we need unbuffered version
+        * of input pipe as FILE*. fclose() closes the underlying fd and
+        * stream buffering only can be changed before first I/O operation
+        * on it.
+        */
+       duped = dup(helper->out);
+       if (duped < 0)
+               die_errno("Can't dup helper output fd");
+       input = xfdopen(duped, "r");
+       setvbuf(input, NULL, _IONBF, 0);
+
+       /*
+        * Handle --upload-pack and friends. This is fire and forget...
+        * just warn if it fails.
+        */
+       if (strcmp(name, exec)) {
+               r = set_helper_option(transport, "servpath", exec);
+               if (r > 0)
+                       warning("Setting remote service path not supported by protocol.");
+               else if (r < 0)
+                       warning("Invalid remote service path.");
+       }
+
+       if (data->connect)
+               strbuf_addf(&cmdbuf, "connect %s\n", name);
+       else
+               goto exit;
+
+       sendline(data, &cmdbuf);
+       recvline_fh(input, &cmdbuf);
+       if (!strcmp(cmdbuf.buf, "")) {
+               data->no_disconnect_req = 1;
+               if (debug)
+                       fprintf(stderr, "Debug: Smart transport connection "
+                               "ready.\n");
+               ret = 1;
+       } else if (!strcmp(cmdbuf.buf, "fallback")) {
+               if (debug)
+                       fprintf(stderr, "Debug: Falling back to dumb "
+                               "transport.\n");
+       } else
+               die("Unknown response to connect: %s",
+                       cmdbuf.buf);
+
+exit:
+       fclose(input);
+       return ret;
+}
+
+static int process_connect(struct transport *transport,
+                                    int for_push)
+{
+       struct helper_data *data = transport->data;
+       const char *name;
+       const char *exec;
+
+       name = for_push ? "git-receive-pack" : "git-upload-pack";
+       if (for_push)
+               exec = data->transport_options.receivepack;
+       else
+               exec = data->transport_options.uploadpack;
+
+       return process_connect_service(transport, name, exec);
+}
+
+static int connect_helper(struct transport *transport, const char *name,
+                  const char *exec, int fd[2])
+{
+       struct helper_data *data = transport->data;
+
+       /* Get_helper so connect is inited. */
+       get_helper(transport);
+       if (!data->connect)
+               die("Operation not supported by protocol.");
+
+       if (!process_connect_service(transport, name, exec))
+               die("Can't connect to subservice %s.", name);
+
+       fd[0] = data->helper->out;
+       fd[1] = data->helper->in;
+       return 0;
+}
+
 static int fetch(struct transport *transport,
                 int nr_heads, struct ref **to_fetch)
 {
        struct helper_data *data = transport->data;
        int i, count;
 
+       if (process_connect(transport, 0)) {
+               do_take_over(transport);
+               return transport->fetch(transport, nr_heads, to_fetch);
+       }
+
        count = 0;
        for (i = 0; i < nr_heads; i++)
                if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
@@ -321,6 +523,11 @@ static int push_refs(struct transport *transport,
        struct child_process *helper;
        struct ref *ref;
 
+       if (process_connect(transport, 1)) {
+               do_take_over(transport);
+               return transport->push_refs(transport, remote_refs, flags);
+       }
+
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
                        "Perhaps you should specify a branch such as 'master'.\n");
@@ -372,17 +579,14 @@ static int push_refs(struct transport *transport,
        }
 
        strbuf_addch(&buf, '\n');
-       if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
-               exit(128);
+       sendline(data, &buf);
 
        ref = remote_refs;
        while (1) {
                char *refname, *msg;
                int status;
 
-               strbuf_reset(&buf);
-               if (strbuf_getline(&buf, data->out, '\n') == EOF)
-                       exit(128); /* child died, message supplied already */
+               recvline(data, &buf);
                if (!buf.len)
                        break;
 
@@ -476,6 +680,11 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 
        helper = get_helper(transport);
 
+       if (process_connect(transport, for_push)) {
+               do_take_over(transport);
+               return transport->get_refs_list(transport, for_push);
+       }
+
        if (data->push && for_push)
                write_str_in_full(helper->in, "list for-push\n");
        else
@@ -483,8 +692,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 
        while (1) {
                char *eov, *eon;
-               if (strbuf_getline(&buf, data->out, '\n') == EOF)
-                       exit(128); /* child died, message supplied already */
+               recvline(data, &buf);
 
                if (!*buf.buf)
                        break;
@@ -509,6 +717,8 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
                }
                tail = &((*tail)->next);
        }
+       if (debug)
+               fprintf(stderr, "Debug: Read ref listing.\n");
        strbuf_release(&buf);
 
        for (posn = ret; posn; posn = posn->next)
@@ -522,11 +732,16 @@ int transport_helper_init(struct transport *transport, const char *name)
        struct helper_data *data = xcalloc(sizeof(*data), 1);
        data->name = name;
 
+       if (getenv("GIT_TRANSPORT_HELPER_DEBUG"))
+               debug = 1;
+
        transport->data = data;
        transport->set_option = set_helper_option;
        transport->get_refs_list = get_refs_list;
        transport->fetch = fetch;
        transport->push_refs = push_refs;
        transport->disconnect = release_helper;
+       transport->connect = connect_helper;
+       transport->smart_options = &(data->transport_options);
        return 0;
 }
index 9b23989117da9d538b8221b02963bbcd17784d6c..d7db2941fb135c8d956756ced762dc0a3dd75a4b 100644 (file)
@@ -143,7 +143,7 @@ static const char *rsync_url(const char *url)
 static struct ref *get_refs_via_rsync(struct transport *transport, int for_push)
 {
        struct strbuf buf = STRBUF_INIT, temp_dir = STRBUF_INIT;
-       struct ref dummy, *tail = &dummy;
+       struct ref dummy = {0}, *tail = &dummy;
        struct child_process rsync;
        const char *args[5];
        int temp_dir_len;
@@ -395,41 +395,36 @@ static int close_bundle(struct transport *transport)
 }
 
 struct git_transport_data {
-       unsigned thin : 1;
-       unsigned keep : 1;
-       unsigned followtags : 1;
-       int depth;
+       struct git_transport_options options;
        struct child_process *conn;
        int fd[2];
-       const char *uploadpack;
-       const char *receivepack;
+       unsigned got_remote_heads : 1;
        struct extra_have_objects extra_have;
 };
 
-static int set_git_option(struct transport *connection,
+static int set_git_option(struct git_transport_options *opts,
                          const char *name, const char *value)
 {
-       struct git_transport_data *data = connection->data;
        if (!strcmp(name, TRANS_OPT_UPLOADPACK)) {
-               data->uploadpack = value;
+               opts->uploadpack = value;
                return 0;
        } else if (!strcmp(name, TRANS_OPT_RECEIVEPACK)) {
-               data->receivepack = value;
+               opts->receivepack = value;
                return 0;
        } else if (!strcmp(name, TRANS_OPT_THIN)) {
-               data->thin = !!value;
+               opts->thin = !!value;
                return 0;
        } else if (!strcmp(name, TRANS_OPT_FOLLOWTAGS)) {
-               data->followtags = !!value;
+               opts->followtags = !!value;
                return 0;
        } else if (!strcmp(name, TRANS_OPT_KEEP)) {
-               data->keep = !!value;
+               opts->keep = !!value;
                return 0;
        } else if (!strcmp(name, TRANS_OPT_DEPTH)) {
                if (!value)
-                       data->depth = 0;
+                       opts->depth = 0;
                else
-                       data->depth = atoi(value);
+                       opts->depth = atoi(value);
                return 0;
        }
        return 1;
@@ -438,9 +433,15 @@ static int set_git_option(struct transport *connection,
 static int connect_setup(struct transport *transport, int for_push, int verbose)
 {
        struct git_transport_data *data = transport->data;
+
+       if (data->conn)
+               return 0;
+
        data->conn = git_connect(data->fd, transport->url,
-                                for_push ? data->receivepack : data->uploadpack,
+                                for_push ? data->options.receivepack :
+                                data->options.uploadpack,
                                 verbose ? CONNECT_VERBOSE : 0);
+
        return 0;
 }
 
@@ -452,6 +453,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
        connect_setup(transport, for_push, 0);
        get_remote_heads(data->fd[0], &refs, 0, NULL,
                         for_push ? REF_NORMAL : 0, &data->extra_have);
+       data->got_remote_heads = 1;
 
        return refs;
 }
@@ -469,22 +471,23 @@ static int fetch_refs_via_pack(struct transport *transport,
        struct ref *refs_tmp = NULL;
 
        memset(&args, 0, sizeof(args));
-       args.uploadpack = data->uploadpack;
-       args.keep_pack = data->keep;
+       args.uploadpack = data->options.uploadpack;
+       args.keep_pack = data->options.keep;
        args.lock_pack = 1;
-       args.use_thin_pack = data->thin;
-       args.include_tag = data->followtags;
+       args.use_thin_pack = data->options.thin;
+       args.include_tag = data->options.followtags;
        args.verbose = (transport->verbose > 0);
        args.quiet = (transport->verbose < 0);
-       args.no_progress = args.quiet || (!transport->progress && !isatty(1));
-       args.depth = data->depth;
+       args.no_progress = args.quiet || (!transport->progress && !isatty(2));
+       args.depth = data->options.depth;
 
        for (i = 0; i < nr_heads; i++)
                origh[i] = heads[i] = xstrdup(to_fetch[i]->name);
 
-       if (!data->conn) {
+       if (!data->got_remote_heads) {
                connect_setup(transport, 0, 0);
                get_remote_heads(data->fd[0], &refs_tmp, 0, NULL, 0, NULL);
+               data->got_remote_heads = 1;
        }
 
        refs = fetch_pack(&args, data->fd, data->conn,
@@ -495,6 +498,7 @@ static int fetch_refs_via_pack(struct transport *transport,
        if (finish_connect(data->conn))
                refs = NULL;
        data->conn = NULL;
+       data->got_remote_heads = 0;
 
        free_refs(refs_tmp);
 
@@ -723,18 +727,19 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
        struct send_pack_args args;
        int ret;
 
-       if (!data->conn) {
+       if (!data->got_remote_heads) {
                struct ref *tmp_refs;
                connect_setup(transport, 1, 0);
 
                get_remote_heads(data->fd[0], &tmp_refs, 0, NULL, REF_NORMAL,
                                 NULL);
+               data->got_remote_heads = 1;
        }
 
        memset(&args, 0, sizeof(args));
        args.send_mirror = !!(flags & TRANSPORT_PUSH_MIRROR);
        args.force_update = !!(flags & TRANSPORT_PUSH_FORCE);
-       args.use_thin_pack = data->thin;
+       args.use_thin_pack = data->options.thin;
        args.verbose = !!(flags & TRANSPORT_PUSH_VERBOSE);
        args.quiet = !!(flags & TRANSPORT_PUSH_QUIET);
        args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
@@ -746,15 +751,28 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
        close(data->fd[0]);
        ret |= finish_connect(data->conn);
        data->conn = NULL;
+       data->got_remote_heads = 0;
 
        return ret;
 }
 
+static int connect_git(struct transport *transport, const char *name,
+                      const char *executable, int fd[2])
+{
+       struct git_transport_data *data = transport->data;
+       data->conn = git_connect(data->fd, transport->url,
+                                executable, 0);
+       fd[0] = data->fd[0];
+       fd[1] = data->fd[1];
+       return 0;
+}
+
 static int disconnect_git(struct transport *transport)
 {
        struct git_transport_data *data = transport->data;
        if (data->conn) {
-               packet_flush(data->fd[1]);
+               if (data->got_remote_heads)
+                       packet_flush(data->fd[1]);
                close(data->fd[0]);
                close(data->fd[1]);
                finish_connect(data->conn);
@@ -764,6 +782,32 @@ static int disconnect_git(struct transport *transport)
        return 0;
 }
 
+void transport_take_over(struct transport *transport,
+                        struct child_process *child)
+{
+       struct git_transport_data *data;
+
+       if (!transport->smart_options)
+               die("Bug detected: Taking over transport requires non-NULL "
+                   "smart_options field.");
+
+       data = xcalloc(1, sizeof(*data));
+       data->options = *transport->smart_options;
+       data->conn = child;
+       data->fd[0] = data->conn->out;
+       data->fd[1] = data->conn->in;
+       data->got_remote_heads = 0;
+       transport->data = data;
+
+       transport->set_option = NULL;
+       transport->get_refs_list = get_refs_via_connect;
+       transport->fetch = fetch_refs_via_pack;
+       transport->push = NULL;
+       transport->push_refs = git_transport_push;
+       transport->disconnect = disconnect_git;
+       transport->smart_options = &(data->options);
+}
+
 static int is_local(const char *url)
 {
        const char *colon = strchr(url, ':');
@@ -780,6 +824,44 @@ static int is_file(const char *url)
        return S_ISREG(buf.st_mode);
 }
 
+static int is_url(const char *url)
+{
+       const char *url2, *first_slash;
+
+       if (!url)
+               return 0;
+       url2 = url;
+       first_slash = strchr(url, '/');
+
+       /* Input with no slash at all or slash first can't be URL. */
+       if (!first_slash || first_slash == url)
+               return 0;
+       /* Character before must be : and next must be /. */
+       if (first_slash[-1] != ':' || first_slash[1] != '/')
+               return 0;
+       /* There must be something before the :// */
+       if (first_slash == url + 1)
+               return 0;
+       /*
+        * Check all characters up to first slash - 1. Only alphanum
+        * is allowed.
+        */
+       url2 = url;
+       while (url2 < first_slash - 1) {
+               if (!isalnum((unsigned char)*url2))
+                       return 0;
+               url2++;
+       }
+
+       /* Valid enough. */
+       return 1;
+}
+
+static int external_specification_len(const char *url)
+{
+       return strchr(url, ':') - url;
+}
+
 struct transport *transport_get(struct remote *remote, const char *url)
 {
        struct transport *ret = xcalloc(1, sizeof(*ret));
@@ -793,6 +875,9 @@ struct transport *transport_get(struct remote *remote, const char *url)
                url = remote->url[0];
        ret->url = url;
 
+       /* In case previous URL had helper forced, reset it. */
+       remote->foreign_vcs = NULL;
+
        /* maybe it is a foreign URL? */
        if (url) {
                const char *p = url;
@@ -805,46 +890,54 @@ struct transport *transport_get(struct remote *remote, const char *url)
 
        if (remote && remote->foreign_vcs) {
                transport_helper_init(ret, remote->foreign_vcs);
-               return ret;
-       }
-
-       if (!prefixcmp(url, "rsync:")) {
+       } else if (!prefixcmp(url, "rsync:")) {
                ret->get_refs_list = get_refs_via_rsync;
                ret->fetch = fetch_objs_via_rsync;
                ret->push = rsync_transport_push;
-
-       } else if (!prefixcmp(url, "http://")
-               || !prefixcmp(url, "https://")
-               || !prefixcmp(url, "ftp://")) {
-               transport_helper_init(ret, "curl");
-#ifdef NO_CURL
-               error("git was compiled without libcurl support.");
-#endif
-
+               ret->smart_options = NULL;
        } else if (is_local(url) && is_file(url)) {
                struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
                ret->data = data;
                ret->get_refs_list = get_refs_from_bundle;
                ret->fetch = fetch_refs_from_bundle;
                ret->disconnect = close_bundle;
-
-       } else {
+               ret->smart_options = NULL;
+       } else if (!is_url(url)
+               || !prefixcmp(url, "file://")
+               || !prefixcmp(url, "git://")
+               || !prefixcmp(url, "ssh://")
+               || !prefixcmp(url, "git+ssh://")
+               || !prefixcmp(url, "ssh+git://")) {
+               /* These are builtin smart transports. */
                struct git_transport_data *data = xcalloc(1, sizeof(*data));
                ret->data = data;
-               ret->set_option = set_git_option;
+               ret->set_option = NULL;
                ret->get_refs_list = get_refs_via_connect;
                ret->fetch = fetch_refs_via_pack;
                ret->push_refs = git_transport_push;
+               ret->connect = connect_git;
                ret->disconnect = disconnect_git;
+               ret->smart_options = &(data->options);
 
-               data->thin = 1;
                data->conn = NULL;
-               data->uploadpack = "git-upload-pack";
+               data->got_remote_heads = 0;
+       } else {
+               /* Unknown protocol in URL. Pass to external handler. */
+               int len = external_specification_len(url);
+               char *handler = xmalloc(len + 1);
+               handler[len] = 0;
+               strncpy(handler, url, len);
+               transport_helper_init(ret, handler);
+       }
+
+       if (ret->smart_options) {
+               ret->smart_options->thin = 1;
+               ret->smart_options->uploadpack = "git-upload-pack";
                if (remote->uploadpack)
-                       data->uploadpack = remote->uploadpack;
-               data->receivepack = "git-receive-pack";
+                       ret->smart_options->uploadpack = remote->uploadpack;
+               ret->smart_options->receivepack = "git-receive-pack";
                if (remote->receivepack)
-                       data->receivepack = remote->receivepack;
+                       ret->smart_options->receivepack = remote->receivepack;
        }
 
        return ret;
@@ -853,8 +946,23 @@ struct transport *transport_get(struct remote *remote, const char *url)
 int transport_set_option(struct transport *transport,
                         const char *name, const char *value)
 {
+       int git_reports = 1, protocol_reports = 1;
+
+       if (transport->smart_options)
+               git_reports = set_git_option(transport->smart_options,
+                                            name, value);
+
        if (transport->set_option)
-               return transport->set_option(transport, name, value);
+               protocol_reports = transport->set_option(transport, name,
+                                                       value);
+
+       /* If either report is 0, report 0 (success). */
+       if (!git_reports || !protocol_reports)
+               return 0;
+       /* If either reports -1 (invalid value), report -1. */
+       if ((git_reports == -1) || (protocol_reports == -1))
+               return -1;
+       /* Otherwise if both report unknown, report unknown. */
        return 1;
 }
 
@@ -865,9 +973,9 @@ int transport_push(struct transport *transport,
        *nonfastforward = 0;
        verify_remote_names(refspec_nr, refspec);
 
-       if (transport->push)
+       if (transport->push) {
                return transport->push(transport, refspec_nr, refspec, flags);
-       if (transport->push_refs) {
+       } else if (transport->push_refs) {
                struct ref *remote_refs =
                        transport->get_refs_list(transport, 1);
                struct ref *local_refs = get_local_heads();
@@ -918,6 +1026,7 @@ const struct ref *transport_get_remote_refs(struct transport *transport)
 {
        if (!transport->remote_refs)
                transport->remote_refs = transport->get_refs_list(transport, 0);
+
        return transport->remote_refs;
 }
 
@@ -952,6 +1061,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
        }
 
        rc = transport->fetch(transport, nr_heads, heads);
+
        free(heads);
        return rc;
 }
@@ -965,6 +1075,15 @@ void transport_unlock_pack(struct transport *transport)
        }
 }
 
+int transport_connect(struct transport *transport, const char *name,
+                     const char *exec, int fd[2])
+{
+       if (transport->connect)
+               return transport->connect(transport, name, exec, fd);
+       else
+               die("Operation not supported by protocol");
+}
+
 int transport_disconnect(struct transport *transport)
 {
        int ret = 0;
index 9e74406fa203a59920cb24884d7893b45d876a70..7a242fe3bd68e55e5807268e5c381c3c89f96a47 100644 (file)
@@ -4,6 +4,15 @@
 #include "cache.h"
 #include "remote.h"
 
+struct git_transport_options {
+       unsigned thin : 1;
+       unsigned keep : 1;
+       unsigned followtags : 1;
+       int depth;
+       const char *uploadpack;
+       const char *receivepack;
+};
+
 struct transport {
        struct remote *remote;
        const char *url;
@@ -55,6 +64,8 @@ struct transport {
         **/
        int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
        int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
+       int (*connect)(struct transport *connection, const char *name,
+                      const char *executable, int fd[2]);
 
        /** get_refs_list(), fetch(), and push_refs() can keep
         * resources (such as a connection) reserved for futher
@@ -63,8 +74,14 @@ struct transport {
        int (*disconnect)(struct transport *connection);
        char *pack_lockfile;
        signed verbose : 3;
-       /* Force progress even if the output is not a tty */
+       /* Force progress even if stderr is not a tty */
        unsigned progress : 1;
+       /*
+        * If transport is at least potentially smart, this points to
+        * git_transport_options structure to use in case transport
+        * actually turns out to be smart.
+        */
+       struct git_transport_options *smart_options;
 };
 
 #define TRANSPORT_PUSH_ALL 1
@@ -115,6 +132,11 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs);
 void transport_unlock_pack(struct transport *transport);
 int transport_disconnect(struct transport *transport);
 char *transport_anonymize_url(const char *url);
+void transport_take_over(struct transport *transport,
+                        struct child_process *child);
+
+int transport_connect(struct transport *transport, const char *name,
+                     const char *exec, int fd[2]);
 
 /* Transport methods defined outside transport.c */
 int transport_helper_init(struct transport *transport, const char *name);
index dd5999c3562219b7993420b22257f4088ab82b8d..0ddbef3e6355da40a1293a6a7c49b4bc5d75b842 100644 (file)
@@ -32,6 +32,12 @@ static struct unpack_trees_error_msgs unpack_plumbing_errors = {
 
        /* bind_overlap */
        "Entry '%s' overlaps with '%s'.  Cannot bind.",
+
+       /* sparse_not_uptodate_file */
+       "Entry '%s' not uptodate. Cannot update sparse checkout.",
+
+       /* would_lose_orphaned */
+       "Working tree file '%s' would be %s by sparse checkout update.",
 };
 
 #define ERRORMSG(o,fld) \
@@ -61,8 +67,16 @@ static void unlink_entry(struct cache_entry *ce)
 {
        if (has_symlink_or_noent_leading_path(ce->name, ce_namelen(ce)))
                return;
-       if (unlink_or_warn(ce->name))
-               return;
+       if (S_ISGITLINK(ce->ce_mode)) {
+               if (rmdir(ce->name)) {
+                       warning("unable to rmdir %s: %s",
+                               ce->name, strerror(errno));
+                       return;
+               }
+       }
+       else
+               if (unlink_or_warn(ce->name))
+                       return;
        schedule_dir_for_removal(ce->name, ce_namelen(ce));
 }
 
@@ -78,7 +92,7 @@ static int check_updates(struct unpack_trees_options *o)
        if (o->update && o->verbose_update) {
                for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
                        struct cache_entry *ce = index->cache[cnt];
-                       if (ce->ce_flags & (CE_UPDATE | CE_REMOVE))
+                       if (ce->ce_flags & (CE_UPDATE | CE_REMOVE | CE_WT_REMOVE))
                                total++;
                }
 
@@ -92,6 +106,13 @@ static int check_updates(struct unpack_trees_options *o)
        for (i = 0; i < index->cache_nr; i++) {
                struct cache_entry *ce = index->cache[i];
 
+               if (ce->ce_flags & CE_WT_REMOVE) {
+                       display_progress(progress, ++cnt);
+                       if (o->update)
+                               unlink_entry(ce);
+                       continue;
+               }
+
                if (ce->ce_flags & CE_REMOVE) {
                        display_progress(progress, ++cnt);
                        if (o->update)
@@ -118,6 +139,57 @@ static int check_updates(struct unpack_trees_options *o)
        return errs != 0;
 }
 
+static int verify_uptodate_sparse(struct cache_entry *ce, struct unpack_trees_options *o);
+static int verify_absent_sparse(struct cache_entry *ce, const char *action, struct unpack_trees_options *o);
+
+static int will_have_skip_worktree(const struct cache_entry *ce, struct unpack_trees_options *o)
+{
+       const char *basename;
+
+       if (ce_stage(ce))
+               return 0;
+
+       basename = strrchr(ce->name, '/');
+       basename = basename ? basename+1 : ce->name;
+       return excluded_from_list(ce->name, ce_namelen(ce), basename, NULL, o->el) <= 0;
+}
+
+static int apply_sparse_checkout(struct cache_entry *ce, struct unpack_trees_options *o)
+{
+       int was_skip_worktree = ce_skip_worktree(ce);
+
+       if (will_have_skip_worktree(ce, o))
+               ce->ce_flags |= CE_SKIP_WORKTREE;
+       else
+               ce->ce_flags &= ~CE_SKIP_WORKTREE;
+
+       /*
+        * We only care about files getting into the checkout area
+        * If merge strategies want to remove some, go ahead, this
+        * flag will be removed eventually in unpack_trees() if it's
+        * outside checkout area.
+        */
+       if (ce->ce_flags & CE_REMOVE)
+               return 0;
+
+       if (!was_skip_worktree && ce_skip_worktree(ce)) {
+               /*
+                * If CE_UPDATE is set, verify_uptodate() must be called already
+                * also stat info may have lost after merged_entry() so calling
+                * verify_uptodate() again may fail
+                */
+               if (!(ce->ce_flags & CE_UPDATE) && verify_uptodate_sparse(ce, o))
+                       return -1;
+               ce->ce_flags |= CE_WT_REMOVE;
+       }
+       if (was_skip_worktree && !ce_skip_worktree(ce)) {
+               if (verify_absent_sparse(ce, "overwritten", o))
+                       return -1;
+               ce->ce_flags |= CE_UPDATE;
+       }
+       return 0;
+}
+
 static inline int call_unpack_fn(struct cache_entry **src, struct unpack_trees_options *o)
 {
        int ret = o->fn(src, o);
@@ -369,8 +441,9 @@ static int unpack_callback(int n, unsigned long mask, unsigned long dirmask, str
  */
 int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options *o)
 {
-       int ret;
+       int i, ret;
        static struct cache_entry *dfc;
+       struct exclude_list el;
 
        if (len > MAX_UNPACK_TREES)
                die("unpack_trees takes at most %d trees", MAX_UNPACK_TREES);
@@ -380,6 +453,16 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
        state.quiet = 1;
        state.refresh_cache = 1;
 
+       memset(&el, 0, sizeof(el));
+       if (!core_apply_sparse_checkout || !o->update)
+               o->skip_sparse_checkout = 1;
+       if (!o->skip_sparse_checkout) {
+               if (add_excludes_from_file_to_list(git_path("info/sparse-checkout"), "", 0, NULL, &el, 0) < 0)
+                       o->skip_sparse_checkout = 1;
+               else
+                       o->el = &el;
+       }
+
        memset(&o->result, 0, sizeof(o->result));
        o->result.initialized = 1;
        if (o->src_index) {
@@ -400,26 +483,65 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                info.fn = unpack_callback;
                info.data = o;
 
-               if (traverse_trees(len, t, &info) < 0)
-                       return unpack_failed(o, NULL);
+               if (traverse_trees(len, t, &info) < 0) {
+                       ret = unpack_failed(o, NULL);
+                       goto done;
+               }
        }
 
        /* Any left-over entries in the index? */
        if (o->merge) {
                while (o->pos < o->src_index->cache_nr) {
                        struct cache_entry *ce = o->src_index->cache[o->pos];
-                       if (unpack_index_entry(ce, o) < 0)
-                               return unpack_failed(o, NULL);
+                       if (unpack_index_entry(ce, o) < 0) {
+                               ret = unpack_failed(o, NULL);
+                               goto done;
+                       }
                }
        }
 
-       if (o->trivial_merges_only && o->nontrivial_merge)
-               return unpack_failed(o, "Merge requires file-level merging");
+       if (o->trivial_merges_only && o->nontrivial_merge) {
+               ret = unpack_failed(o, "Merge requires file-level merging");
+               goto done;
+       }
+
+       if (!o->skip_sparse_checkout) {
+               int empty_worktree = 1;
+               for (i = 0;i < o->result.cache_nr;i++) {
+                       struct cache_entry *ce = o->result.cache[i];
+
+                       if (apply_sparse_checkout(ce, o)) {
+                               ret = -1;
+                               goto done;
+                       }
+                       /*
+                        * Merge strategies may set CE_UPDATE|CE_REMOVE outside checkout
+                        * area as a result of ce_skip_worktree() shortcuts in
+                        * verify_absent() and verify_uptodate(). Clear them.
+                        */
+                       if (ce_skip_worktree(ce))
+                               ce->ce_flags &= ~(CE_UPDATE | CE_REMOVE);
+                       else
+                               empty_worktree = 0;
+
+               }
+               if (o->result.cache_nr && empty_worktree) {
+                       ret = unpack_failed(o, "Sparse checkout leaves no entry on working directory");
+                       goto done;
+               }
+       }
 
        o->src_index = NULL;
        ret = check_updates(o) ? (-2) : 0;
        if (o->dst_index)
                *o->dst_index = o->result;
+
+done:
+       for (i = 0;i < el.nr;i++)
+               free(el.excludes[i]);
+       if (el.excludes)
+               free(el.excludes);
+
        return ret;
 }
 
@@ -436,6 +558,8 @@ static int same(struct cache_entry *a, struct cache_entry *b)
                return 0;
        if (!a && !b)
                return 1;
+       if ((a->ce_flags | b->ce_flags) & CE_CONFLICTED)
+               return 0;
        return a->ce_mode == b->ce_mode &&
               !hashcmp(a->sha1, b->sha1);
 }
@@ -445,16 +569,17 @@ static int same(struct cache_entry *a, struct cache_entry *b)
  * When a CE gets turned into an unmerged entry, we
  * want it to be up-to-date
  */
-static int verify_uptodate(struct cache_entry *ce,
-               struct unpack_trees_options *o)
+static int verify_uptodate_1(struct cache_entry *ce,
+                                  struct unpack_trees_options *o,
+                                  const char *error_msg)
 {
        struct stat st;
 
-       if (o->index_only || o->reset || ce_uptodate(ce))
+       if (o->index_only || (!ce_skip_worktree(ce) && (o->reset || ce_uptodate(ce))))
                return 0;
 
        if (!lstat(ce->name, &st)) {
-               unsigned changed = ie_match_stat(o->src_index, ce, &st, CE_MATCH_IGNORE_VALID);
+               unsigned changed = ie_match_stat(o->src_index, ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
                if (!changed)
                        return 0;
                /*
@@ -471,7 +596,21 @@ static int verify_uptodate(struct cache_entry *ce,
        if (errno == ENOENT)
                return 0;
        return o->gently ? -1 :
-               error(ERRORMSG(o, not_uptodate_file), ce->name);
+               error(error_msg, ce->name);
+}
+
+static int verify_uptodate(struct cache_entry *ce,
+                          struct unpack_trees_options *o)
+{
+       if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))
+               return 0;
+       return verify_uptodate_1(ce, o, ERRORMSG(o, not_uptodate_file));
+}
+
+static int verify_uptodate_sparse(struct cache_entry *ce,
+                                 struct unpack_trees_options *o)
+{
+       return verify_uptodate_1(ce, o, ERRORMSG(o, sparse_not_uptodate_file));
 }
 
 static void invalidate_ce_path(struct cache_entry *ce, struct unpack_trees_options *o)
@@ -572,15 +711,16 @@ static int icase_exists(struct unpack_trees_options *o, struct cache_entry *dst,
        struct cache_entry *src;
 
        src = index_name_exists(o->src_index, dst->name, ce_namelen(dst), 1);
-       return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID);
+       return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
 }
 
 /*
  * We do not want to remove or overwrite a working tree file that
  * is not tracked, unless it is ignored.
  */
-static int verify_absent(struct cache_entry *ce, const char *action,
-                        struct unpack_trees_options *o)
+static int verify_absent_1(struct cache_entry *ce, const char *action,
+                                struct unpack_trees_options *o,
+                                const char *error_msg)
 {
        struct stat st;
 
@@ -660,13 +800,30 @@ static int verify_absent(struct cache_entry *ce, const char *action,
        }
        return 0;
 }
+static int verify_absent(struct cache_entry *ce, const char *action,
+                        struct unpack_trees_options *o)
+{
+       if (!o->skip_sparse_checkout && will_have_skip_worktree(ce, o))
+               return 0;
+       return verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_untracked));
+}
+
+static int verify_absent_sparse(struct cache_entry *ce, const char *action,
+                        struct unpack_trees_options *o)
+{
+       return verify_absent_1(ce, action, o, ERRORMSG(o, would_lose_orphaned));
+}
 
 static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                struct unpack_trees_options *o)
 {
        int update = CE_UPDATE;
 
-       if (old) {
+       if (!old) {
+               if (verify_absent(merge, "overwritten", o))
+                       return -1;
+               invalidate_ce_path(merge, o);
+       } else if (!(old->ce_flags & CE_CONFLICTED)) {
                /*
                 * See if we can re-use the old CE directly?
                 * That way we get the uptodate stat info.
@@ -680,13 +837,16 @@ static int merged_entry(struct cache_entry *merge, struct cache_entry *old,
                } else {
                        if (verify_uptodate(old, o))
                                return -1;
+                       if (ce_skip_worktree(old))
+                               update |= CE_SKIP_WORKTREE;
                        invalidate_ce_path(old, o);
                }
-       }
-       else {
-               if (verify_absent(merge, "overwritten", o))
-                       return -1;
-               invalidate_ce_path(merge, o);
+       } else {
+               /*
+                * Previously unmerged entry left as an existence
+                * marker by read_index_unmerged();
+                */
+               invalidate_ce_path(old, o);
        }
 
        add_entry(o, merge, update, CE_STAGEMASK);
@@ -702,7 +862,7 @@ static int deleted_entry(struct cache_entry *ce, struct cache_entry *old,
                        return -1;
                return 0;
        }
-       if (verify_uptodate(old, o))
+       if (!(old->ce_flags & CE_CONFLICTED) && verify_uptodate(old, o))
                return -1;
        add_entry(o, ce, CE_REMOVE, 0);
        invalidate_ce_path(ce, o);
@@ -1004,10 +1164,10 @@ int oneway_merge(struct cache_entry **src, struct unpack_trees_options *o)
 
        if (old && same(old, a)) {
                int update = 0;
-               if (o->reset && !ce_uptodate(old)) {
+               if (o->reset && !ce_uptodate(old) && !ce_skip_worktree(old)) {
                        struct stat st;
                        if (lstat(old->name, &st) ||
-                           ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID))
+                           ie_match_stat(o->src_index, old, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE))
                                update |= CE_UPDATE;
                }
                add_entry(o, old, update, 0);
index d19df44f4070ca1bda29382e56fbced53aacd994..95ff36c824a14a24234459ad552306fb1b5dabf1 100644 (file)
@@ -4,6 +4,7 @@
 #define MAX_UNPACK_TREES 8
 
 struct unpack_trees_options;
+struct exclude_list;
 
 typedef int (*merge_fn_t)(struct cache_entry **src,
                struct unpack_trees_options *options);
@@ -14,6 +15,8 @@ struct unpack_trees_error_msgs {
        const char *not_uptodate_dir;
        const char *would_lose_untracked;
        const char *bind_overlap;
+       const char *sparse_not_uptodate_file;
+       const char *would_lose_orphaned;
 };
 
 struct unpack_trees_options {
@@ -28,6 +31,7 @@ struct unpack_trees_options {
                     skip_unmerged,
                     initial_checkout,
                     diff_index_cached,
+                    skip_sparse_checkout,
                     gently;
        const char *prefix;
        int pos;
@@ -44,6 +48,8 @@ struct unpack_trees_options {
        struct index_state *dst_index;
        struct index_state *src_index;
        struct index_state result;
+
+       struct exclude_list *el; /* for internal use */
 };
 
 extern int unpack_trees(unsigned n, struct tree_desc *t,
diff --git a/utf8.c b/utf8.c
index 7ddff23fa77fbadf7723bca03d24ad5b8f2baca2..ab326ac83e0d9e81b06abff58b00a98341adcd36 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -163,7 +163,7 @@ static int git_wcwidth(ucs_char_t ch)
  * If the string was not a valid UTF-8, *start pointer is set to NULL
  * and the return value is undefined.
  */
-ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p)
+static ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p)
 {
        unsigned char *s = (unsigned char *)*start;
        ucs_char_t ch;
diff --git a/utf8.h b/utf8.h
index ae30ae4c6e501e4766db93c94253fd404cd29357..c9738d83d991d89bbdd4c5a6442fcad2fdaaa4df 100644 (file)
--- a/utf8.h
+++ b/utf8.h
@@ -3,7 +3,6 @@
 
 typedef unsigned int ucs_char_t;  /* assuming 32bit int */
 
-ucs_char_t pick_one_utf8_char(const char **start, size_t *remainder_p);
 int utf8_width(const char **start, size_t *remainder_p);
 int utf8_strwidth(const char *string);
 int is_utf8(const char *text);
diff --git a/wrap-for-bin.sh b/wrap-for-bin.sh
new file mode 100644 (file)
index 0000000..c5075c9
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# wrap-for-bin.sh: Template for git executable wrapper scripts
+# to run test suite against sandbox, but with only bindir-installed
+# executables in PATH.  The Makefile copies this into various
+# files in bin-wrappers, substituting
+# @@BUILD_DIR@@ and @@PROG@@.
+
+GIT_EXEC_PATH='@@BUILD_DIR@@'
+GIT_TEMPLATE_DIR='@@BUILD_DIR@@/templates/blt'
+GITPERLLIB='@@BUILD_DIR@@/perl/blib/lib'
+PATH='@@BUILD_DIR@@/bin-wrappers:'"$PATH"
+export GIT_EXEC_PATH GIT_TEMPLATE_DIR GITPERLLIB PATH
+
+exec "${GIT_EXEC_PATH}/@@PROG@@" "$@"