Merge branch 'master' into il/vcs-helper
authorJunio C Hamano <gitster@pobox.com>
Mon, 7 Dec 2009 06:40:16 +0000 (22:40 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 7 Dec 2009 06:40:16 +0000 (22:40 -0800)
* master: (334 commits)
bash: update 'git commit' completion
Git 1.6.5.5
Fix diff -B/--dirstat miscounting of newly added contents
reset: improve worktree safety valves
Documentation: Avoid use of xmlto --stringparam
archive: clarify description of path parameter
rerere: don't segfault on failure to open rr-cache
Prepare for 1.6.5.5
gitweb: Describe (possible) gitweb.js minification in gitweb/README
Documentation: xmlto 0.0.18 does not know --stringparam
Fix crasher on encountering SHA1-like non-note in notes tree
t9001: use older Getopt::Long boolean prefix '--no' rather than '--no-'
t4201: use ISO8859-1 rather than ISO-8859-1
Git 1.6.5.4
Unconditionally set man.base.url.for.relative.links
Documentation/Makefile: allow man.base.url.for.relative.link to be set from Make
Git 1.6.6-rc1
git-pull.sh: Fix call to git-merge for new command format
Prepare for 1.6.5.4
merge: do not add standard message when message is given with -m option
...

Conflicts:
Documentation/git-remote-helpers.txt
Makefile
builtin-ls-remote.c
builtin-push.c
transport-helper.c

Signed-off-by: Junio C Hamano <gitster@pobox.com>
14 files changed:
1  2 
Documentation/config.txt
Documentation/git-remote-helpers.txt
Makefile
builtin-clone.c
builtin-fetch.c
builtin-ls-remote.c
builtin-push.c
configure.ac
remote.c
remote.h
t/test-lib.sh
transport-helper.c
transport.c
transport.h
diff --combined Documentation/config.txt
index 0d9d369ca7637c0c2b9d4130a470b70b35143dae,a1e36d7e423e01de796ac5f866536280618199d1..76173dbc5deaf1fdbff316b42e0a42ce61f4f32d
@@@ -126,12 -126,20 +126,20 @@@ advice.*:
                Directions on how to stage/unstage/add shown in the
                output of linkgit:git-status[1] and the template shown
                when writing commit messages. Default: true.
+       commitBeforeMerge::
+               Advice shown when linkgit:git-merge[1] refuses to
+               merge to avoid overwritting local changes.
+               Default: true.
  --
  
  core.fileMode::
        If false, the executable bit differences between the index and
        the working copy are ignored; useful on broken filesystems like FAT.
-       See linkgit:git-update-index[1]. True by default.
+       See linkgit:git-update-index[1].
+ +
+ The default is true, except linkgit:git-clone[1] or linkgit:git-init[1]
+ will probe and set core.fileMode false if appropriate when the
+ repository is created.
  
  core.ignoreCygwinFSTricks::
        This option is only used by Cygwin implementation of Git. If false,
        is true, in which case ignoreCygwinFSTricks is ignored as Cygwin's
        POSIX emulation is required to support core.filemode.
  
+ core.ignorecase::
+       If true, this option enables various workarounds to enable
+       git to work better on filesystems that are not case sensitive,
+       like FAT. For example, if a directory listing finds
+       "makefile" when git expects "Makefile", git will assume
+       it is really the same file, and continue to remember it as
+       "Makefile".
+ +
+ The default is false, except linkgit:git-clone[1] or linkgit:git-init[1]
+ will probe and set core.ignorecase true if appropriate when the repository
+ is created.
  core.trustctime::
        If false, the ctime differences between the index and the
        working copy are ignored; useful when the inode change time
@@@ -169,9 -189,10 +189,10 @@@ core.autocrlf:
        writing to the filesystem.  The variable can be set to
        'input', in which case the conversion happens only while
        reading from the filesystem but files are written out with
-       `LF` at the end of lines.  Currently, which paths to consider
-       "text" (i.e. be subjected to the autocrlf mechanism) is
-       decided purely based on the contents.
+       `LF` at the end of lines.  A file is considered
+       "text" (i.e. be subjected to the autocrlf mechanism) based on
+       the file's `crlf` attribute, or if `crlf` is unspecified,
+       based on the file's contents.  See linkgit:gitattributes[5].
  
  core.safecrlf::
        If true, makes git check if converting `CRLF` as controlled by
@@@ -223,7 -244,11 +244,11 @@@ core.symlinks:
        contain the link text. linkgit:git-update-index[1] and
        linkgit:git-add[1] will not change the recorded type to regular
        file. Useful on filesystems like FAT that do not support
-       symbolic links. True by default.
+       symbolic links.
+ +
+ The default is true, except linkgit:git-clone[1] or linkgit:git-init[1]
+ will probe and set core.symlinks false if appropriate when the repository
+ is created.
  
  core.gitProxy::
        A "proxy command" to execute (as 'command host port') instead
@@@ -380,16 -405,15 +405,15 @@@ Common unit suffixes of 'k', 'm', or 'g
  core.excludesfile::
        In addition to '.gitignore' (per-directory) and
        '.git/info/exclude', git looks into this file for patterns
-       of files which are not meant to be tracked.  See
-       linkgit:gitignore[5].
+       of files which are not meant to be tracked.  "{tilde}/" is expanded
+       to the value of `$HOME` and "{tilde}user/" to the specified user's
+       home directory.  See linkgit:gitignore[5].
  
  core.editor::
        Commands such as `commit` and `tag` that lets you edit
        messages by launching an editor uses the value of this
        variable when it is set, and the environment variable
-       `GIT_EDITOR` is not set.  The order of preference is
-       `GIT_EDITOR` environment, `core.editor`, `VISUAL` and
-       `EDITOR` environment variables and then finally `vi`.
+       `GIT_EDITOR` is not set.  See linkgit:git-var[1].
  
  core.pager::
        The command that git will use to paginate output.  Can
@@@ -458,6 -482,19 +482,19 @@@ On some file system/operating system co
  Set this config setting to 'rename' there; However, This will remove the
  check that makes sure that existing object files will not get overwritten.
  
+ core.notesRef::
+       When showing commit messages, also show notes which are stored in
+       the given ref.  This ref is expected to contain files named
+       after the full SHA-1 of the commit they annotate.
+ +
+ If such a file exists in the given ref, the referenced blob is read, and
+ appended to the commit message, separated by a "Notes:" line.  If the
+ given ref itself does not exist, it is not an error, but means that no
+ notes should be printed.
+ +
+ This setting defaults to "refs/notes/commits", and can be overridden by
+ the `GIT_NOTES_REF` environment variable.
  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'
@@@ -598,10 -635,10 +635,10 @@@ color.diff.<slot>:
        Use customized color for diff colorization.  `<slot>` specifies
        which part of the patch to use the specified color, and is one
        of `plain` (context text), `meta` (metainformation), `frag`
-       (hunk header), `old` (removed lines), `new` (added lines),
-       `commit` (commit headers), or `whitespace` (highlighting
-       whitespace errors). The values of these variables may be specified as
-       in color.branch.<slot>.
+       (hunk header), 'func' (function in hunk header), `old` (removed lines),
+       `new` (added lines), `commit` (commit headers), or `whitespace`
+       (highlighting whitespace errors). The values of these variables may be
+       specified as in color.branch.<slot>.
  
  color.grep::
        When set to `always`, always highlight matches.  When `false` (or
@@@ -670,6 -707,8 +707,8 @@@ color.ui:
  
  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
+       specified user's home directory.
  
  diff.autorefreshindex::
        When using 'git-diff' to compare with work tree
@@@ -1093,6 -1132,14 +1132,14 @@@ 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.postBuffer::
+       Maximum size in bytes of the buffer used by smart HTTP
+       transports when POSTing data to the remote system.
+       For requests larger than this buffer size, HTTP/1.1 and
+       Transfer-Encoding: chunked is used to avoid creating a
+       massive pack file locally.  Default is 1 MiB, which is
+       sufficient for most requests.
  http.lowSpeedLimit, http.lowSpeedTime::
        If the HTTP transfer speed is less than 'http.lowSpeedLimit'
        for longer than 'http.lowSpeedTime' seconds, the transfer is aborted.
@@@ -1360,7 -1407,7 +1407,7 @@@ receive.denyCurrentBranch:
  
  receive.denyNonFastForwards::
        If set to true, git-receive-pack will deny a ref update which is
-       not a fast forward. Use this to prevent such an update via a push,
+       not a fast-forward. Use this to prevent such an update via a push,
        even if that push is forced. This configuration variable is
        set when initializing a shared repository.
  
@@@ -1394,7 -1441,13 +1441,13 @@@ remote.<name>.mirror:
  
  remote.<name>.skipDefaultUpdate::
        If true, this remote will be skipped by default when updating
-       using the update subcommand of linkgit:git-remote[1].
+       using linkgit:git-fetch[1] or the `update` subcommand of
+       linkgit:git-remote[1].
+ remote.<name>.skipFetchAll::
+       If true, this remote will be skipped by default when updating
+       using linkgit:git-fetch[1] or the `update` subcommand of
+       linkgit:git-remote[1].
  
  remote.<name>.receivepack::
        The default program to execute on the remote side when pushing.  See
@@@ -1408,10 -1461,6 +1461,10 @@@ remote.<name>.tagopt:
        Setting this value to \--no-tags disables automatic tag following when
        fetching from remote <name>
  
 +remote.<name>.vcs::
 +      Setting this to a value <vcs> will cause git to interact with
 +      the remote with the git-remote-<vcs> helper.
 +
  remotes.<group>::
        The list of remotes which are fetched by "git remote update
        <group>".  See linkgit:git-remote[1].
index f4b6a5aea3bb3b1076877cffa8beb17936114468,8beb42dbb9952059650d51d2e7a5ca2efe9591fb..5cfdc0cfc553dc01f6f1c7cb185e79449ee0b0a8
@@@ -34,26 -34,51 +34,62 @@@ Commands are given by the caller on th
        value of the ref. A space-separated list of attributes follows
        the name; unrecognized attributes are ignored. After the
        complete list, outputs a blank line.
+ +
+ If 'push' is supported this may be called as 'list for-push'
+ to obtain the current refs prior to sending one or more 'push'
+ commands to the helper.
+ 'option' <name> <value>::
+       Set the transport helper option <name> to <value>.  Outputs a
+       single line containing one of 'ok' (option successfully set),
+       'unsupported' (option not recognized) or 'error <msg>'
+       (option <name> is supported but <value> is not correct
+       for it).  Options should be set before other commands,
+       and may how those commands behave.
+ +
+ Supported if the helper has the "option" capability.
  
  'fetch' <sha1> <name>::
-       Fetches the given object, writing the necessary objects to the
-       database. Outputs a blank line when the fetch is
-       complete. Only objects which were reported in the ref list
-       with a sha1 may be fetched this way.
+       Fetches the given object, writing the necessary objects
+       to the database.  Fetch commands are sent in a batch, one
+       per line, and the batch is terminated with a blank line.
+       Outputs a single blank line when all fetch commands in the
+       same batch are complete. Only objects which were reported
+       in the ref list with a sha1 may be fetched this way.
+ +
+ Optionally may output a 'lock <file>' line indicating a file under
+ GIT_DIR/objects/pack which is keeping a pack until refs can be
+ suitably updated.
  +
  Supported if the helper has the "fetch" capability.
  
+ 'push' +<src>:<dst>::
+       Pushes the given <src> commit or branch locally to the
+       remote branch described by <dst>.  A batch sequence of
+       one or more push commands is terminated with a blank line.
+ +
+ Zero or more protocol options may be entered after the last 'push'
+ command, before the batch's terminating blank line.
+ +
+ When the push is complete, outputs one or more 'ok <dst>' or
+ 'error <dst> <why>?' lines to indicate success or failure of
+ each pushed ref.  The status report output is terminated by
+ a blank line.  The option field <why> may be quoted in a C
+ style string if it contains an LF.
+ +
+ Supported if the helper has the "push" capability.
 +'import' <name>::
 +      Produces a fast-import stream which imports the current value
 +      of the named ref. It may additionally import other refs as
 +      needed to construct the history efficiently. The script writes
 +      to a helper-specific private namespace. The value of the named
 +      ref should be written to a location in this namespace derived
 +      by applying the refspecs from the "refspec" capability to the
 +      name of the ref.
 ++
 +Supported if the helper has the "import" 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
@@@ -68,26 -93,50 +104,67 @@@ CAPABILITIE
  'fetch'::
        This helper supports the 'fetch' command.
  
+ 'option'::
+       This helper supports the option command.
+ 'push'::
+       This helper supports the 'push' command.
 +'import'::
 +      This helper supports the 'import' command.
 +
 +'refspec' 'spec'::
 +      When using the import command, expect the source ref to have
 +      been written to the destination ref. The earliest applicable
 +      refspec takes precedence. For example
 +      "refs/heads/*:refs/svn/origin/branches/*" means that, after an
 +      "import refs/heads/name", the script has written to
 +      refs/svn/origin/branches/name. If this capability is used at
 +      all, it must cover all refs reported by the list command; if
 +      it is not used, it is effectively "*:*"
 +
  REF LIST ATTRIBUTES
  -------------------
  
+ 'for-push'::
+       The caller wants to use the ref list to prepare push
+       commands.  A helper might chose to acquire the ref list by
+       opening a different type of connection to the destination.
 +'unchanged'::
 +      This ref is unchanged since the last import or fetch, although
 +      the helper cannot necessarily determine what value that produced.
 +
+ OPTIONS
+ -------
+ 'option verbosity' <N>::
+       Change the level of messages displayed by the helper.
+       When N is 0 the end-user has asked the process to be
+       quiet, and the helper should produce only error output.
+       N of 1 is the default level of verbosity, higher values
+       of N correspond to the number of -v flags passed on the
+       command line.
+ 'option progress' \{'true'|'false'\}::
+       Enable (or disable) progress messages displayed by the
+       transport helper during a command.
+ 'option depth' <depth>::
+       Deepen the history of a shallow repository.
+ 'option followtags' \{'true'|'false'\}::
+       If enabled the helper should automatically fetch annotated
+       tag objects if the object the tag points at was transferred
+       during the fetch command.  If the tag is not fetched by
+       the helper a second fetch command will usually be sent to
+       ask for the tag specifically.  Some helpers may be able to
+       use this option to avoid a second network connection.
+ 'option dry-run' \{'true'|'false'\}:
+       If true, pretend the operation completed successfully,
+       but don't actually change any repository data.  For most
+       helpers this only applies to the 'push', if supported.
  Documentation
  -------------
  Documentation by Daniel Barkalow.
diff --combined Makefile
index a95ffda23a488e60b091fcf5fc17027c6ecf5922,4a1e5bcc4def58a5d8f05a368fea69cde8ddb74e..2ad7e366ef925f99b8a903f3f3a936ee72052127
+++ b/Makefile
@@@ -159,13 -159,15 +159,17 @@@ all:
  # Define ASCIIDOC_NO_ROFF if your DocBook XSL escapes raw roff directives
  # (versions 1.72 and later and 1.68.1 and earlier).
  #
+ # Define GNU_ROFF if your target system uses GNU groff.  This forces
+ # apostrophes to be ASCII so that cut&pasting examples to the shell
+ # will work.
+ #
  # Define NO_PERL_MAKEMAKER if you cannot use Makefiles generated by perl's
  # MakeMaker (e.g. using ActiveState under Cygwin).
  #
  # Define NO_PERL if you do not want Perl scripts or libraries at all.
  #
 +# Define NO_PYTHON if you do not want Python scripts or libraries at all.
 +#
  # Define NO_TCLTK if you do not want Tcl/Tk GUI.
  #
  # The TCL_PATH variable governs the location of the Tcl interpreter
  # memory allocators with the nedmalloc allocator written by Niall Douglas.
  #
  # Define NO_REGEX if you have no or inferior regex support in your C library.
+ #
+ # Define JSMIN to point to JavaScript minifier that functions as
+ # a filter to have gitweb.js minified.
+ #
+ # Define DEFAULT_PAGER to a sensible pager command (defaults to "less") if
+ # you want to use something different.  The value will be interpreted by the
+ # shell at runtime when it is used.
+ #
+ # Define DEFAULT_EDITOR to a sensible editor command (defaults to "vi") if you
+ # want to use something different.  The value will be interpreted by the shell
+ # if necessary when it is used.  Examples:
+ #
+ #   DEFAULT_EDITOR='~/bin/vi',
+ #   DEFAULT_EDITOR='$GIT_FALLBACK_EDITOR',
+ #   DEFAULT_EDITOR='"C:\Program Files\Vim\gvim.exe" --nofork'
  
  GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@@ -214,6 -231,12 +233,12 @@@ uname_R := $(shell sh -c 'uname -r 2>/d
  uname_P := $(shell sh -c 'uname -p 2>/dev/null || echo not')
  uname_V := $(shell sh -c 'uname -v 2>/dev/null || echo not')
  
+ ifdef MSVC
+       # avoid the MingW and Cygwin configuration sections
+       uname_S := Windows
+       uname_O := Windows
+ endif
  # CFLAGS and LDFLAGS are for the users to override from the command line.
  
  CFLAGS = -g -O2 -Wall
@@@ -254,6 -277,9 +279,9 @@@ lib = li
  # DESTDIR=
  pathsep = :
  
+ # JavaScript minifier invocation that can function as filter
+ JSMIN =
  # default configuration for gitweb
  GITWEB_CONFIG = gitweb_config.perl
  GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf
@@@ -269,6 -295,11 +297,11 @@@ GITWEB_HOMETEXT = indextext.htm
  GITWEB_CSS = gitweb.css
  GITWEB_LOGO = git-logo.png
  GITWEB_FAVICON = git-favicon.png
+ ifdef JSMIN
+ GITWEB_JS = gitweb.min.js
+ else
+ GITWEB_JS = gitweb.js
+ endif
  GITWEB_SITE_HEADER =
  GITWEB_SITE_FOOTER =
  
@@@ -310,7 -341,6 +343,7 @@@ LIB_H 
  LIB_OBJS =
  PROGRAMS =
  SCRIPT_PERL =
 +SCRIPT_PYTHON =
  SCRIPT_SH =
  TEST_PROGRAMS =
  
@@@ -324,6 -354,7 +357,7 @@@ SCRIPT_SH += git-merge-one-file.s
  SCRIPT_SH += git-merge-resolve.sh
  SCRIPT_SH += git-mergetool.sh
  SCRIPT_SH += git-mergetool--lib.sh
+ SCRIPT_SH += git-notes.sh
  SCRIPT_SH += git-parse-remote.sh
  SCRIPT_SH += git-pull.sh
  SCRIPT_SH += git-quiltimport.sh
@@@ -348,7 -379,6 +382,7 @@@ SCRIPT_PERL += git-svn.per
  
  SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
 +        $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
          git-instaweb
  
  # Empty...
@@@ -358,6 -388,7 +392,7 @@@ EXTRA_PROGRAMS 
  PROGRAMS += $(EXTRA_PROGRAMS)
  PROGRAMS += git-fast-import$X
  PROGRAMS += git-hash-object$X
+ PROGRAMS += git-imap-send$X
  PROGRAMS += git-index-pack$X
  PROGRAMS += git-merge-index$X
  PROGRAMS += git-merge-tree$X
@@@ -369,6 -400,7 +404,7 @@@ PROGRAMS += git-show-index$
  PROGRAMS += git-unpack-file$X
  PROGRAMS += git-upload-pack$X
  PROGRAMS += git-var$X
+ PROGRAMS += git-http-backend$X
  
  # List built-in command $C whose implementation cmd_$C() is not in
  # builtin-$C.o but is linked in as part of some other command.
@@@ -402,12 -434,8 +438,12 @@@ endi
  ifndef PERL_PATH
        PERL_PATH = /usr/bin/perl
  endif
 +ifndef PYTHON_PATH
 +      PYTHON_PATH = /usr/bin/python
 +endif
  
  export PERL_PATH
 +export PYTHON_PATH
  
  LIB_FILE=libgit.a
  XDIFF_LIB=xdiff/lib.a
@@@ -420,6 -448,7 +456,7 @@@ LIB_H += builtin.
  LIB_H += cache.h
  LIB_H += cache-tree.h
  LIB_H += commit.h
+ LIB_H += compat/bswap.h
  LIB_H += compat/cygwin.h
  LIB_H += compat/mingw.h
  LIB_H += csum-file.h
@@@ -440,6 -469,7 +477,7 @@@ LIB_H += ll-merge.
  LIB_H += log-tree.h
  LIB_H += mailmap.h
  LIB_H += merge-recursive.h
+ LIB_H += notes.h
  LIB_H += object.h
  LIB_H += pack.h
  LIB_H += pack-refs.h
@@@ -460,6 -490,7 +498,7 @@@ LIB_H += sideband.
  LIB_H += sigchain.h
  LIB_H += strbuf.h
  LIB_H += string-list.h
+ LIB_H += submodule.h
  LIB_H += tag.h
  LIB_H += transport.h
  LIB_H += tree.h
@@@ -524,6 -555,7 +563,7 @@@ LIB_OBJS += match-trees.
  LIB_OBJS += merge-file.o
  LIB_OBJS += merge-recursive.o
  LIB_OBJS += name-hash.o
+ LIB_OBJS += notes.o
  LIB_OBJS += object.o
  LIB_OBJS += pack-check.o
  LIB_OBJS += pack-refs.o
@@@ -558,6 -590,7 +598,7 @@@ LIB_OBJS += sideband.
  LIB_OBJS += sigchain.o
  LIB_OBJS += strbuf.o
  LIB_OBJS += string-list.o
+ LIB_OBJS += submodule.o
  LIB_OBJS += symlinks.o
  LIB_OBJS += tag.o
  LIB_OBJS += trace.o
@@@ -602,7 -635,6 +643,6 @@@ BUILTIN_OBJS += builtin-diff-index.
  BUILTIN_OBJS += builtin-diff-tree.o
  BUILTIN_OBJS += builtin-diff.o
  BUILTIN_OBJS += builtin-fast-export.o
- BUILTIN_OBJS += builtin-fetch--tool.o
  BUILTIN_OBJS += builtin-fetch-pack.o
  BUILTIN_OBJS += builtin-fetch.o
  BUILTIN_OBJS += builtin-fmt-merge-msg.o
@@@ -784,12 -816,15 +824,15 @@@ ifeq ($(uname_O),Cygwin
        NO_FAST_WORKING_DIRECTORY = UnfortunatelyYes
        NO_TRUSTABLE_FILEMODE = UnfortunatelyYes
        OLD_ICONV = UnfortunatelyYes
+       NO_ST_BLOCKS_IN_STRUCT_STAT = YesPlease
        # There are conflicting reports about this.
        # On some boxes NO_MMAP is needed, and not so elsewhere.
        # Try commenting this out if you suspect MMAP is more efficient
        NO_MMAP = YesPlease
        NO_IPV6 = YesPlease
        X = .exe
+       COMPAT_OBJS += compat/cygwin.o
+       UNRELIABLE_FSTAT = UnfortunatelyYes
  endif
  ifeq ($(uname_S),FreeBSD)
        NEEDS_LIBICONV = YesPlease
@@@ -899,15 -934,11 +942,11 @@@ ifeq ($(uname_S),HP-UX
        NO_SYS_SELECT_H = YesPlease
        SNPRINTF_RETURNS_BOGUS = YesPlease
  endif
- ifneq (,$(findstring CYGWIN,$(uname_S)))
-       COMPAT_OBJS += compat/cygwin.o
-       UNRELIABLE_FSTAT = UnfortunatelyYes
- endif
- ifdef MSVC
+ ifeq ($(uname_S),Windows)
        GIT_VERSION := $(GIT_VERSION).MSVC
        pathsep = ;
        NO_PREAD = YesPlease
-       NO_OPENSSL = YesPlease
+       NEEDS_CRYPTO_WITH_SSL = YesPlease
        NO_LIBGEN_H = YesPlease
        NO_SYMLINK_HEAD = YesPlease
        NO_IPV6 = YesPlease
        NO_REGEX = YesPlease
        NO_CURL = YesPlease
        NO_PTHREADS = YesPlease
+       BLK_SHA1 = YesPlease
  
        CC = compat/vcbuild/scripts/clink.pl
        AR = compat/vcbuild/scripts/lib.pl
@@@ -955,14 -987,13 +995,13 @@@ els
        BASIC_CFLAGS += -Zi -MTd
  endif
        X = .exe
- else
+ endif
  ifneq (,$(findstring MINGW,$(uname_S)))
        pathsep = ;
        NO_PREAD = YesPlease
-       NO_OPENSSL = YesPlease
+       NEEDS_CRYPTO_WITH_SSL = YesPlease
        NO_LIBGEN_H = YesPlease
        NO_SYMLINK_HEAD = YesPlease
-       NO_IPV6 = YesPlease
        NO_SETENV = YesPlease
        NO_UNSETENV = YesPlease
        NO_STRCASESTR = YesPlease
        UNRELIABLE_FSTAT = UnfortunatelyYes
        OBJECT_CREATION_USES_RENAMES = UnfortunatelyNeedsTo
        NO_REGEX = YesPlease
+       BLK_SHA1 = 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
@@@ -1004,7 -1036,6 +1044,6 @@@ els
        NO_PTHREADS = YesPlease
  endif
  endif
- endif
  
  -include config.mak.autogen
  -include config.mak
@@@ -1083,7 -1114,6 +1122,6 @@@ EXTLIBS += -l
  
  ifndef NO_POSIX_ONLY_PROGRAMS
        PROGRAMS += git-daemon$X
-       PROGRAMS += git-imap-send$X
  endif
  ifndef NO_OPENSSL
        OPENSSL_LIBSSL = -lssl
@@@ -1316,10 -1346,6 +1354,10 @@@ ifeq ($(PERL_PATH),
  NO_PERL=NoThanks
  endif
  
 +ifeq ($(PYTHON_PATH),)
 +NO_PYTHON=NoThanks
 +endif
 +
  QUIET_SUBDIR0  = +$(MAKE) -C # space to separate -C and subdir
  QUIET_SUBDIR1  =
  
@@@ -1367,7 -1393,6 +1405,7 @@@ prefix_SQ = $(subst ','\'',$(prefix)
  
  SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
  PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 +PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
  TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
  
  LIBS = $(GITLIBS) $(EXTLIBS)
@@@ -1376,6 -1401,22 +1414,22 @@@ BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_H
        $(COMPAT_CFLAGS)
  LIB_OBJS += $(COMPAT_OBJS)
  
+ # Quote for C
+ ifdef DEFAULT_EDITOR
+ DEFAULT_EDITOR_CQ = "$(subst ",\",$(subst \,\\,$(DEFAULT_EDITOR)))"
+ DEFAULT_EDITOR_CQ_SQ = $(subst ','\'',$(DEFAULT_EDITOR_CQ))
+ BASIC_CFLAGS += -DDEFAULT_EDITOR='$(DEFAULT_EDITOR_CQ_SQ)'
+ endif
+ ifdef DEFAULT_PAGER
+ DEFAULT_PAGER_CQ = "$(subst ",\",$(subst \,\\,$(DEFAULT_PAGER)))"
+ DEFAULT_PAGER_CQ_SQ = $(subst ','\'',$(DEFAULT_PAGER_CQ))
+ BASIC_CFLAGS += -DDEFAULT_PAGER='$(DEFAULT_PAGER_CQ_SQ)'
+ endif
  ALL_CFLAGS += $(BASIC_CFLAGS)
  ALL_LDFLAGS += $(BASIC_LDFLAGS)
  
@@@ -1398,9 -1439,6 +1452,9 @@@ ifndef NO_TCLT
  endif
  ifndef NO_PERL
        $(QUIET_SUBDIR0)perl $(QUIET_SUBDIR1) PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
 +endif
 +ifndef NO_PYTHON
 +      $(QUIET_SUBDIR0)git_remote_helpers $(QUIET_SUBDIR1) PYTHON_PATH='$(PYTHON_PATH_SQ)' prefix='$(prefix_SQ)' all
  endif
        $(QUIET_SUBDIR0)templates $(QUIET_SUBDIR1)
  
@@@ -1471,8 -1509,13 +1525,13 @@@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % 
        chmod +x $@+ && \
        mv $@+ $@
  
+ ifdef JSMIN
+ OTHER_PROGRAMS += gitweb/gitweb.cgi   gitweb/gitweb.min.js
+ gitweb/gitweb.cgi: gitweb/gitweb.perl gitweb/gitweb.min.js
+ else
  OTHER_PROGRAMS += gitweb/gitweb.cgi
  gitweb/gitweb.cgi: gitweb/gitweb.perl
+ endif
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*perl|#!$(PERL_PATH_SQ)|' \
            -e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
            -e 's|++GITWEB_CSS++|$(GITWEB_CSS)|g' \
            -e 's|++GITWEB_LOGO++|$(GITWEB_LOGO)|g' \
            -e 's|++GITWEB_FAVICON++|$(GITWEB_FAVICON)|g' \
+           -e 's|++GITWEB_JS++|$(GITWEB_JS)|g' \
            -e 's|++GITWEB_SITE_HEADER++|$(GITWEB_SITE_HEADER)|g' \
            -e 's|++GITWEB_SITE_FOOTER++|$(GITWEB_SITE_FOOTER)|g' \
            $< >$@+ && \
        chmod +x $@+ && \
        mv $@+ $@
  
- git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css
+ git-instaweb: git-instaweb.sh gitweb/gitweb.cgi gitweb/gitweb.css gitweb/gitweb.js
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
            -e '/@@GITWEB_CGI@@/d' \
            -e '/@@GITWEB_CSS@@/r gitweb/gitweb.css' \
            -e '/@@GITWEB_CSS@@/d' \
+           -e '/@@GITWEB_JS@@/r gitweb/gitweb.js' \
+           -e '/@@GITWEB_JS@@/d' \
            -e 's|@@PERL@@|$(PERL_PATH_SQ)|g' \
            $@.sh > $@+ && \
        chmod +x $@+ && \
@@@ -1520,35 -1566,11 +1582,41 @@@ $(patsubst %.perl,%,$(SCRIPT_PERL)) git
        mv $@+ $@
  endif # NO_PERL
  
++
+ ifdef JSMIN
+ gitweb/gitweb.min.js: gitweb/gitweb.js
+       $(QUIET_GEN)$(JSMIN) <$< >$@
+ endif # JSMIN
 +ifndef NO_PYTHON
 +$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS
 +$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
 +      $(QUIET_GEN)$(RM) $@ $@+ && \
 +      INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
 +              --no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \
 +              instlibdir` && \
 +      sed -e '1{' \
 +          -e '        s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
 +          -e '}' \
 +          -e 's|^import sys.*|&; \\\
 +                 import os; \\\
 +                 sys.path[0] = os.environ.has_key("GITPYTHONLIB") and \\\
 +                               os.environ["GITPYTHONLIB"] or \\\
 +                               "@@INSTLIBDIR@@"|' \
 +          -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
 +          $@.py >$@+ && \
 +      chmod +x $@+ && \
 +      mv $@+ $@
 +else # NO_PYTHON
 +$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : unimplemented.sh
 +      $(QUIET_GEN)$(RM) $@ $@+ && \
 +      sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
 +          -e 's|@@REASON@@|NO_PYTHON=$(NO_PYTHON)|g' \
 +          unimplemented.sh >$@+ && \
 +      chmod +x $@+ && \
 +      mv $@+ $@
 +endif # NO_PYTHON
 +
  configure: configure.ac
        $(QUIET_GEN)$(RM) $@ $<+ && \
        sed -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
@@@ -1671,6 -1693,7 +1739,7 @@@ GIT-CFLAGS: .FORCE-GIT-CFLAG
  # and the first level quoting from the shell that runs "echo".
  GIT-BUILD-OPTIONS: .FORCE-GIT-BUILD-OPTIONS
        @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@
+       @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@
        @echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@
        @echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
@@@ -1773,9 -1796,6 +1842,9 @@@ install: al
  ifndef NO_PERL
        $(MAKE) -C perl prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
  endif
 +ifndef NO_PYTHON
 +      $(MAKE) -C git_remote_helpers prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' install
 +endif
  ifndef NO_TCLTK
        $(MAKE) -C gitk-git install
        $(MAKE) -C git-gui gitexecdir='$(gitexec_instdir_SQ)' install
@@@ -1847,7 -1867,10 +1916,10 @@@ dist: git.spec git-archive$(X) configur
        gzip -f -9 $(GIT_TARNAME).tar
  
  rpm: dist
-       $(RPMBUILD) -ta $(GIT_TARNAME).tar.gz
+       $(RPMBUILD) \
+               --define "_source_filedigest_algorithm md5" \
+               --define "_binary_filedigest_algorithm md5" \
+               -ta $(GIT_TARNAME).tar.gz
  
  htmldocs = git-htmldocs-$(GIT_VERSION)
  manpages = git-manpages-$(GIT_VERSION)
@@@ -1875,7 -1898,7 +1947,7 @@@ distclean: clea
        $(RM) configure
  
  clean:
-       $(RM) *.o block-sha1/*.o arm/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o \
+       $(RM) *.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o \
                $(LIB_FILE) $(XDIFF_LIB)
        $(RM) $(ALL_PROGRAMS) $(BUILT_INS) git$X
        $(RM) $(TEST_PROGRAMS)
  ifndef NO_PERL
        $(RM) gitweb/gitweb.cgi
        $(MAKE) -C perl clean
 +endif
 +ifndef NO_PYTHON
 +      $(MAKE) -C git_remote_helpers clean
  endif
        $(MAKE) -C templates/ clean
        $(MAKE) -C t/ clean
diff --combined builtin-clone.c
index 32f22e100ed792fffe1aa3c59c3f841f2fee24ab,caf3025031d83dc860b22c7798dd5f40b6b89ed2..5df8b0f72c8bb5dfa7fa7df1edfec90405031f47
@@@ -51,7 -51,9 +51,9 @@@ static struct option builtin_clone_opti
        OPT_BOOLEAN('n', "no-checkout", &option_no_checkout,
                    "don't create a checkout"),
        OPT_BOOLEAN(0, "bare", &option_bare, "create a bare repository"),
-       OPT_BOOLEAN(0, "naked", &option_bare, "create a bare repository"),
+       { OPTION_BOOLEAN, 0, "naked", &option_bare, NULL,
+               "create a bare repository",
+               PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
        OPT_BOOLEAN(0, "mirror", &option_mirror,
                    "create a mirror repository (implies bare)"),
        OPT_BOOLEAN('l', "local", &option_local,
@@@ -61,7 -63,7 +63,7 @@@
        OPT_BOOLEAN('s', "shared", &option_shared,
                    "setup as shared repository"),
        OPT_BOOLEAN(0, "recursive", &option_recursive,
-                   "setup as shared repository"),
+                   "initialize submodules in the clone"),
        OPT_STRING(0, "template", &option_template, "path",
                   "path the template repository"),
        OPT_STRING(0, "reference", &option_reference, "repo",
@@@ -360,10 -362,9 +362,10 @@@ int cmd_clone(int argc, const char **ar
        const char *repo_name, *repo, *work_tree, *git_dir;
        char *path, *dir;
        int dest_exists;
 -      const struct ref *refs, *remote_head, *mapped_refs;
 +      const struct ref *refs, *remote_head;
        const struct ref *remote_head_points_at;
        const struct ref *our_head_points_at;
 +      struct ref *mapped_refs;
        struct strbuf key = STRBUF_INIT, value = STRBUF_INIT;
        struct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;
        struct transport *transport = NULL;
        argc = parse_options(argc, argv, prefix, builtin_clone_options,
                             builtin_clone_usage, 0);
  
+       if (argc > 2)
+               usage_msg_opt("Too many arguments.",
+                       builtin_clone_usage, builtin_clone_options);
        if (argc == 0)
-               die("You must specify a repository to clone.");
+               usage_msg_opt("You must specify a repository to clone.",
+                       builtin_clone_usage, builtin_clone_options);
  
        if (option_mirror)
                option_bare = 1;
diff --combined builtin-fetch.c
index 013a6ba1b9d4522863d22516a6134142bbcaaa68,5b7db616dcf5cc3bb178b2c4dbb989322c6b2374..8654fa7a2dbe2c1ca76a493a4322447de5219b99
@@@ -14,6 -14,9 +14,9 @@@
  
  static const char * const builtin_fetch_usage[] = {
        "git fetch [options] [<repository> <refspec>...]",
+       "git fetch [options] <group>",
+       "git fetch --multiple [options] [<repository> | <group>]...",
+       "git fetch --all [options]",
        NULL
  };
  
@@@ -23,7 -26,7 +26,7 @@@ enum 
        TAGS_SET = 2
  };
  
- static int append, force, keep, update_head_ok, verbosity;
+ static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
  static int tags = TAGS_DEFAULT;
  static const char *depth;
  static const char *upload_pack;
@@@ -32,16 -35,24 +35,24 @@@ static struct transport *transport
  
  static struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
+       OPT_BOOLEAN(0, "all", &all,
+                   "fetch from all remotes"),
        OPT_BOOLEAN('a', "append", &append,
                    "append to .git/FETCH_HEAD instead of overwriting"),
        OPT_STRING(0, "upload-pack", &upload_pack, "PATH",
                   "path to upload pack on remote end"),
        OPT_BOOLEAN('f', "force", &force,
                    "force overwrite of local branch"),
+       OPT_BOOLEAN('m', "multiple", &multiple,
+                   "fetch from multiple remotes"),
        OPT_SET_INT('t', "tags", &tags,
                    "fetch all tags and associated objects", TAGS_SET),
        OPT_SET_INT('n', NULL, &tags,
                    "do not fetch all tags (--no-tags)", TAGS_UNSET),
+       OPT_BOOLEAN('p', "prune", &prune,
+                   "prune tracking branches no longer on remote"),
+       OPT_BOOLEAN(0, "dry-run", &dry_run,
+                   "dry run"),
        OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
        OPT_BOOLEAN('u', "update-head-ok", &update_head_ok,
                    "allow updating of HEAD ref"),
@@@ -178,6 -189,8 +189,8 @@@ static int s_update_ref(const char *act
        char *rla = getenv("GIT_REFLOG_ACTION");
        static struct ref_lock *lock;
  
+       if (dry_run)
+               return 0;
        if (!rla)
                rla = default_rla.buf;
        snprintf(msg, sizeof(msg), "%s: %s", rla, action);
@@@ -269,7 -282,7 +282,7 @@@ static int update_local_ref(struct ref 
                strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
                strcat(quickref, "..");
                strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
-               r = s_update_ref("fast forward", ref, 1);
+               r = s_update_ref("fast-forward", ref, 1);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
                        SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
                        pretty_ref, r ? "  (unable to update local ref)" : "");
                        r ? "unable to update local ref" : "forced update");
                return r;
        } else {
-               sprintf(display, "! %-*s %-*s -> %s  (non fast forward)",
+               sprintf(display, "! %-*s %-*s -> %s  (non-fast-forward)",
                        SUMMARY_WIDTH, "[rejected]", REFCOL_WIDTH, remote,
                        pretty_ref);
                return 1;
@@@ -303,16 -316,13 +316,16 @@@ static int store_updated_refs(const cha
        char note[1024];
        const char *what, *kind;
        struct ref *rm;
-       char *url, *filename = git_path("FETCH_HEAD");
+       char *url, *filename = dry_run ? "/dev/null" : git_path("FETCH_HEAD");
  
        fp = fopen(filename, "a");
        if (!fp)
                return error("cannot open %s: %s\n", filename, strerror(errno));
  
 -      url = transport_anonymize_url(raw_url);
 +      if (raw_url)
 +              url = transport_anonymize_url(raw_url);
 +      else
 +              url = xstrdup("foreign");
        for (rm = ref_map; rm; rm = rm->next) {
                struct ref *ref = NULL;
  
@@@ -488,11 -498,34 +501,34 @@@ static int fetch_refs(struct transport 
        return ret;
  }
  
+ static int prune_refs(struct transport *transport, struct ref *ref_map)
+ {
+       int result = 0;
+       struct ref *ref, *stale_refs = get_stale_heads(transport->remote, ref_map);
+       const char *dangling_msg = dry_run
+               ? "   (%s will become dangling)\n"
+               : "   (%s has become dangling)\n";
+       for (ref = stale_refs; ref; ref = ref->next) {
+               if (!dry_run)
+                       result |= delete_ref(ref->name, NULL, 0);
+               if (verbosity >= 0) {
+                       fprintf(stderr, " x %-*s %-*s -> %s\n",
+                               SUMMARY_WIDTH, "[deleted]",
+                               REFCOL_WIDTH, "(none)", prettify_refname(ref->name));
+                       warn_dangling_symref(stderr, dangling_msg, ref->name);
+               }
+       }
+       free_refs(stale_refs);
+       return result;
+ }
  static int add_existing(const char *refname, const unsigned char *sha1,
                        int flag, void *cbdata)
  {
        struct string_list *list = (struct string_list *)cbdata;
-       string_list_insert(refname, list);
+       struct string_list_item *item = string_list_insert(refname, list);
+       item->util = (void *)sha1;
        return 0;
  }
  
@@@ -618,9 -651,14 +654,14 @@@ static void check_not_current_branch(st
  static int do_fetch(struct transport *transport,
                    struct refspec *refs, int ref_count)
  {
+       struct string_list existing_refs = { NULL, 0, 0, 0 };
+       struct string_list_item *peer_item = NULL;
        struct ref *ref_map;
        struct ref *rm;
        int autotags = (transport->remote->fetch_tags == 1);
+       for_each_ref(add_existing, &existing_refs);
        if (transport->remote->fetch_tags == 2 && tags != TAGS_UNSET)
                tags = TAGS_SET;
        if (transport->remote->fetch_tags == -1)
                die("Don't know how to fetch from %s", transport->url);
  
        /* if not appending, truncate FETCH_HEAD */
-       if (!append) {
+       if (!append && !dry_run) {
                char *filename = git_path("FETCH_HEAD");
                FILE *fp = fopen(filename, "w");
                if (!fp)
                check_not_current_branch(ref_map);
  
        for (rm = ref_map; rm; rm = rm->next) {
-               if (rm->peer_ref)
-                       read_ref(rm->peer_ref->name, rm->peer_ref->old_sha1);
+               if (rm->peer_ref) {
+                       peer_item = string_list_lookup(rm->peer_ref->name,
+                                                      &existing_refs);
+                       if (peer_item)
+                               hashcpy(rm->peer_ref->old_sha1,
+                                       peer_item->util);
+               }
        }
  
        if (tags == TAGS_DEFAULT && autotags)
                free_refs(ref_map);
                return 1;
        }
+       if (prune)
+               prune_refs(transport, ref_map);
        free_refs(ref_map);
  
        /* if neither --no-tags nor --tags was specified, do automated tag
@@@ -683,33 -728,100 +731,100 @@@ static void set_option(const char *name
                        name, transport->url);
  }
  
- int cmd_fetch(int argc, const char **argv, const char *prefix)
+ static int get_one_remote_for_fetch(struct remote *remote, void *priv)
+ {
+       struct string_list *list = priv;
+       if (!remote->skip_default_update)
+               string_list_append(remote->name, list);
+       return 0;
+ }
+ struct remote_group_data {
+       const char *name;
+       struct string_list *list;
+ };
+ static int get_remote_group(const char *key, const char *value, void *priv)
+ {
+       struct remote_group_data *g = priv;
+       if (!prefixcmp(key, "remotes.") &&
+                       !strcmp(key + 8, g->name)) {
+               /* split list by white space */
+               int space = strcspn(value, " \t\n");
+               while (*value) {
+                       if (space > 1) {
+                               string_list_append(xstrndup(value, space),
+                                                  g->list);
+                       }
+                       value += space + (value[space] != '\0');
+                       space = strcspn(value, " \t\n");
+               }
+       }
+       return 0;
+ }
+ static int add_remote_or_group(const char *name, struct string_list *list)
+ {
+       int prev_nr = list->nr;
+       struct remote_group_data g = { name, list };
+       git_config(get_remote_group, &g);
+       if (list->nr == prev_nr) {
+               struct remote *remote;
+               if (!remote_is_configured(name))
+                       return 0;
+               remote = remote_get(name);
+               string_list_append(remote->name, list);
+       }
+       return 1;
+ }
+ static int fetch_multiple(struct string_list *list)
+ {
+       int i, result = 0;
+       const char *argv[] = { "fetch", NULL, NULL, NULL, NULL, NULL, NULL };
+       int argc = 1;
+       if (dry_run)
+               argv[argc++] = "--dry-run";
+       if (prune)
+               argv[argc++] = "--prune";
+       if (verbosity >= 2)
+               argv[argc++] = "-v";
+       if (verbosity >= 1)
+               argv[argc++] = "-v";
+       else if (verbosity < 0)
+               argv[argc++] = "-q";
+       for (i = 0; i < list->nr; i++) {
+               const char *name = list->items[i].string;
+               argv[argc] = name;
+               if (verbosity >= 0)
+                       printf("Fetching %s\n", name);
+               if (run_command_v_opt(argv, RUN_GIT_CMD)) {
+                       error("Could not fetch %s", name);
+                       result = 1;
+               }
+       }
+       return result;
+ }
+ static int fetch_one(struct remote *remote, int argc, const char **argv)
  {
-       struct remote *remote;
        int i;
        static const char **refs = NULL;
        int ref_nr = 0;
        int exit_code;
  
-       /* Record the command line for the reflog */
-       strbuf_addstr(&default_rla, "fetch");
-       for (i = 1; i < argc; i++)
-               strbuf_addf(&default_rla, " %s", argv[i]);
-       argc = parse_options(argc, argv, prefix,
-                            builtin_fetch_options, builtin_fetch_usage, 0);
-       if (argc == 0)
-               remote = remote_get(NULL);
-       else
-               remote = remote_get(argv[0]);
        if (!remote)
                die("Where do you want to fetch from today?");
  
 -      transport = transport_get(remote, remote->url[0]);
 +      transport = transport_get(remote, NULL);
        if (verbosity >= 2)
-               transport->verbose = 1;
+               transport->verbose = verbosity <= 3 ? verbosity : 3;
        if (verbosity < 0)
                transport->verbose = -1;
        if (upload_pack)
        if (depth)
                set_option(TRANS_OPT_DEPTH, depth);
  
-       if (argc > 1) {
+       if (argc > 0) {
                int j = 0;
                refs = xcalloc(argc + 1, sizeof(const char *));
-               for (i = 1; i < argc; i++) {
+               for (i = 0; i < argc; i++) {
                        if (!strcmp(argv[i], "tag")) {
                                char *ref;
                                i++;
        transport = NULL;
        return exit_code;
  }
+ int cmd_fetch(int argc, const char **argv, const char *prefix)
+ {
+       int i;
+       struct string_list list = { NULL, 0, 0, 0 };
+       struct remote *remote;
+       int result = 0;
+       /* Record the command line for the reflog */
+       strbuf_addstr(&default_rla, "fetch");
+       for (i = 1; i < argc; i++)
+               strbuf_addf(&default_rla, " %s", argv[i]);
+       argc = parse_options(argc, argv, prefix,
+                            builtin_fetch_options, builtin_fetch_usage, 0);
+       if (all) {
+               if (argc == 1)
+                       die("fetch --all does not take a repository argument");
+               else if (argc > 1)
+                       die("fetch --all does not make sense with refspecs");
+               (void) for_each_remote(get_one_remote_for_fetch, &list);
+               result = fetch_multiple(&list);
+       } else if (argc == 0) {
+               /* No arguments -- use default remote */
+               remote = remote_get(NULL);
+               result = fetch_one(remote, argc, argv);
+       } else if (multiple) {
+               /* All arguments are assumed to be remotes or groups */
+               for (i = 0; i < argc; i++)
+                       if (!add_remote_or_group(argv[i], &list))
+                               die("No such remote or remote group: %s", argv[i]);
+               result = fetch_multiple(&list);
+       } else {
+               /* Single remote or group */
+               (void) add_remote_or_group(argv[0], &list);
+               if (list.nr > 1) {
+                       /* More than one remote */
+                       if (argc > 1)
+                               die("Fetching a group and specifying refspecs does not make sense");
+                       result = fetch_multiple(&list);
+               } else {
+                       /* Zero or one remotes */
+                       remote = remote_get(argv[0]);
+                       result = fetch_one(remote, argc-1, argv+1);
+               }
+       }
+       /* All names were strdup()ed or strndup()ed */
+       list.strdup_strings = 1;
+       string_list_clear(&list, 0);
+       return result;
+ }
diff --combined builtin-ls-remote.c
index d625df2f4e6c44f1986b0a090d923e7fee78878e,b5bad0c184fc1ebc49759f211781ecd4031fd027..70f5622d9d49aae4080b38ee4487cc6e403a9d2c
@@@ -86,10 -86,10 +86,10 @@@ int cmd_ls_remote(int argc, const char 
                        pattern[j - i] = p;
                }
        }
-       remote = nongit ? NULL : remote_get(dest);
-       if (remote && !remote->url_nr)
+       remote = remote_get(dest);
+       if (!remote->url_nr)
                die("remote %s has no configured URL", dest);
 -      transport = transport_get(remote, remote->url[0]);
 +      transport = transport_get(remote, NULL);
        if (uploadpack != NULL)
                transport_set_option(transport, TRANS_OPT_UPLOADPACK, uploadpack);
  
diff --combined builtin-push.c
index 03be045baed585d4d3996b0d16ea96cf79e2a584,356d7c1fd3a6fae9558e93eb4f240242a60da368..dcfb53f1884d2648993214b4378afbd1f83760b0
@@@ -66,7 -66,6 +66,6 @@@ static void setup_push_tracking(void
  
  static void setup_default_push_refspecs(void)
  {
-       git_config(git_default_config, NULL);
        switch (push_default) {
        default:
        case PUSH_DEFAULT_MATCHING:
        }
  }
  
 +static int push_with_options(struct transport *transport, int flags)
 +{
 +      int err;
 +      int nonfastforward;
 +      if (receivepack)
 +              transport_set_option(transport,
 +                                   TRANS_OPT_RECEIVEPACK, receivepack);
 +      if (thin)
 +              transport_set_option(transport, TRANS_OPT_THIN, "yes");
 +
 +      if (flags & TRANSPORT_PUSH_VERBOSE)
 +              fprintf(stderr, "Pushing to %s\n", transport->url);
 +      err = transport_push(transport, refspec_nr, refspec, flags,
 +                           &nonfastforward);
 +      if (err != 0)
 +              error("failed to push some refs to '%s'", transport->url);
 +
 +      err |= transport_disconnect(transport);
 +
 +      if (!err)
 +              return 0;
 +
 +      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");
 +      }
 +
 +      return 1;
 +}
 +
  static int do_push(const char *repo, int flags)
  {
        int i, errs;
                url = remote->url;
                url_nr = remote->url_nr;
        }
 -      for (i = 0; i < url_nr; i++) {
 -              struct transport *transport =
 -                      transport_get(remote, url[i]);
 -              int err;
 -              int nonfastforward;
 -              if (receivepack)
 -                      transport_set_option(transport,
 -                                           TRANS_OPT_RECEIVEPACK, receivepack);
 -              if (thin)
 -                      transport_set_option(transport, TRANS_OPT_THIN, "yes");
 -
 -              if (flags & TRANSPORT_PUSH_VERBOSE)
 -                      fprintf(stderr, "Pushing to %s\n", url[i]);
 -              err = transport_push(transport, refspec_nr, refspec, flags,
 -                                   &nonfastforward);
 -              err |= transport_disconnect(transport);
 -
 -              if (!err)
 -                      continue;
 -
 -              error("failed to push some refs to '%s'", url[i]);
 -              if (nonfastforward && 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");
 +      if (url_nr) {
 +              for (i = 0; i < url_nr; i++) {
 +                      struct transport *transport =
 +                              transport_get(remote, url[i]);
 +                      if (push_with_options(transport, flags))
 +                              errs++;
                }
 -              errs++;
 +      } else {
 +              struct transport *transport =
 +                      transport_get(remote, NULL);
 +
 +              if (push_with_options(transport, flags))
 +                      errs++;
        }
        return !!errs;
  }
@@@ -190,7 -172,6 +189,6 @@@ int cmd_push(int argc, const char **arg
        int tags = 0;
        int rc;
        const char *repo = NULL;        /* default repository */
        struct option options[] = {
                OPT_BIT('q', "quiet", &flags, "be quiet", TRANSPORT_PUSH_QUIET),
                OPT_BIT('v', "verbose", &flags, "be verbose", TRANSPORT_PUSH_VERBOSE),
                OPT_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 , "tags", &tags, "push tags (can't be used with --all or --mirror"),
+               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),
                OPT_BIT('f', "force", &flags, "force updates", TRANSPORT_PUSH_FORCE),
                OPT_END()
        };
  
+       git_config(git_default_config, NULL);
        argc = parse_options(argc, argv, prefix, options, push_usage, 0);
  
        if (tags)
diff --combined configure.ac
index 84b6cf48a4f9829c32bb14c80b667193a173ae5a,4625b8672bf5b0dcc977737c2c5086c5c1aa54a6..78345ebb60240e6642f319467defe3e07d4aa263
@@@ -68,6 -68,26 +68,26 @@@ else 
        GIT_CONF_APPEND_LINE(${PACKAGE}DIR=$withval); \
  fi \
  ])# GIT_PARSE_WITH
+ #
+ # GIT_PARSE_WITH_SET_MAKE_VAR(WITHNAME, VAR, HELP_TEXT)
+ # ---------------------
+ # Set VAR to the value specied by --with-WITHNAME.
+ # No verification of arguments is performed, but warnings are issued
+ # if either 'yes' or 'no' is specified.
+ # HELP_TEXT is presented when --help is called.
+ # This is a direct way to allow setting variables in the Makefile.
+ AC_DEFUN([GIT_PARSE_WITH_SET_MAKE_VAR],
+ [AC_ARG_WITH([$1],
+  [AS_HELP_STRING([--with-$1=VALUE], $3)],
+  if test -n "$withval"; then \
+   if test "$withval" = "yes" -o "$withval" = "no"; then \
+     AC_MSG_WARN([You likely do not want either 'yes' or 'no' as]
+                    [a value for $1 ($2).  Maybe you do...?]); \
+   fi; \
+   \
+   AC_MSG_NOTICE([Setting $2 to $withval]); \
+   GIT_CONF_APPEND_LINE($2=$withval); \
+  fi)])# GIT_PARSE_WITH_SET_MAKE_VAR
  
  dnl
  dnl GIT_CHECK_FUNC(FUNCTION, IFTRUE, IFFALSE)
@@@ -226,6 -246,29 +246,29 @@@ GIT_PARSE_WITH(iconv)
  # Define USE_STDEV below if you want git to care about the underlying device
  # change being considered an inode change from the update-index perspective.
  
+ #
+ # Allow user to set ETC_GITCONFIG variable
+ GIT_PARSE_WITH_SET_MAKE_VAR(gitconfig, ETC_GITCONFIG,
+                       Use VALUE instead of /etc/gitconfig as the
+                       global git configuration file.
+                       If VALUE is not fully qualified it will be interpretted
+                       as a path relative to the computed prefix at runtime.)
+ #
+ # Allow user to set the default pager
+ GIT_PARSE_WITH_SET_MAKE_VAR(pager, DEFAULT_PAGER,
+                       Use VALUE as the fall-back pager instead of 'less'.
+                       This is used by things like 'git log' when the user
+                       does not specify a pager to use through alternate
+                       methods. eg: /usr/bin/pager)
+ #
+ # Allow user to set the default editor
+ GIT_PARSE_WITH_SET_MAKE_VAR(editor, DEFAULT_EDITOR,
+                       Use VALUE as the fall-back editor instead of 'vi'.
+                       This is used by things like 'git commit' when the user
+                       does not specify a preferred editor through other
+                       methods. eg: /usr/bin/editor)
  #
  # Define SHELL_PATH to provide path to shell.
  GIT_ARG_SET_PATH(shell)
  # Define PERL_PATH to provide path to Perl.
  GIT_ARG_SET_PATH(perl)
  #
 +# Define PYTHON_PATH to provide path to Python.
 +GIT_ARG_SET_PATH(python)
 +#
  # Define ZLIB_PATH to provide path to zlib.
  GIT_ARG_SET_PATH(zlib)
  #
diff --combined remote.c
index 1f7870d107371af9dd4db1fb6e2e777138d682b3,b979a9642b81b456ab92cdf57e8eda944f01f92e..e3afecdb1069a244493e906b5e16a96dc47535d3
+++ b/remote.c
@@@ -6,6 -6,7 +6,7 @@@
  #include "revision.h"
  #include "dir.h"
  #include "tag.h"
+ #include "string-list.h"
  
  static struct refspec s_tag_refspec = {
        0,
@@@ -52,11 -53,6 +53,11 @@@ static struct rewrites rewrites_push
  #define BUF_SIZE (2048)
  static char buffer[BUF_SIZE];
  
 +static int valid_remote(const struct remote *remote)
 +{
 +      return (!!remote->url) || (!!remote->foreign_vcs);
 +}
 +
  static const char *alias_url(const char *url, struct rewrites *r)
  {
        int i, j;
@@@ -401,7 -397,8 +402,8 @@@ static int handle_config(const char *ke
                remote->mirror = git_config_bool(key, value);
        else if (!strcmp(subkey, ".skipdefaultupdate"))
                remote->skip_default_update = git_config_bool(key, value);
+       else if (!strcmp(subkey, ".skipfetchall"))
+               remote->skip_default_update = git_config_bool(key, value);
        else if (!strcmp(subkey, ".url")) {
                const char *v;
                if (git_config_string(&v, key, value))
        } else if (!strcmp(subkey, ".proxy")) {
                return git_config_string((const char **)&remote->http_proxy,
                                         key, value);
 +      } else if (!strcmp(subkey, ".vcs")) {
 +              return git_config_string(&remote->foreign_vcs, key, value);
        }
        return 0;
  }
@@@ -673,16 -668,6 +675,16 @@@ static struct refspec *parse_push_refsp
        return parse_refspec_internal(nr_refspec, refspec, 0, 0);
  }
  
 +void free_refspec(int nr_refspec, struct refspec *refspec)
 +{
 +      int i;
 +      for (i = 0; i < nr_refspec; i++) {
 +              free(refspec[i].src);
 +              free(refspec[i].dst);
 +      }
 +      free(refspec);
 +}
 +
  static int valid_remote_nick(const char *name)
  {
        if (!name[0] || is_dot_or_dotdot(name))
@@@ -705,14 -690,14 +707,14 @@@ struct remote *remote_get(const char *n
  
        ret = make_remote(name, 0);
        if (valid_remote_nick(name)) {
 -              if (!ret->url)
 +              if (!valid_remote(ret))
                        read_remotes_file(ret);
 -              if (!ret->url)
 +              if (!valid_remote(ret))
                        read_branches_file(ret);
        }
 -      if (name_given && !ret->url)
 +      if (name_given && !valid_remote(ret))
                add_url_alias(ret, name);
 -      if (!ret->url)
 +      if (!valid_remote(ret))
                return NULL;
        ret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);
        ret->push = parse_push_refspec(ret->push_refspec_nr, ret->push_refspec);
@@@ -751,29 -736,33 +753,33 @@@ int for_each_remote(each_remote_fn fn, 
  
  void ref_remove_duplicates(struct ref *ref_map)
  {
-       struct ref **posn;
-       struct ref *next;
-       for (; ref_map; ref_map = ref_map->next) {
+       struct string_list refs = { NULL, 0, 0, 0 };
+       struct string_list_item *item = NULL;
+       struct ref *prev = NULL, *next = NULL;
+       for (; ref_map; prev = ref_map, ref_map = next) {
+               next = ref_map->next;
                if (!ref_map->peer_ref)
                        continue;
-               posn = &ref_map->next;
-               while (*posn) {
-                       if ((*posn)->peer_ref &&
-                           !strcmp((*posn)->peer_ref->name,
-                                   ref_map->peer_ref->name)) {
-                               if (strcmp((*posn)->name, ref_map->name))
-                                       die("%s tracks both %s and %s",
-                                           ref_map->peer_ref->name,
-                                           (*posn)->name, ref_map->name);
-                               next = (*posn)->next;
-                               free((*posn)->peer_ref);
-                               free(*posn);
-                               *posn = next;
-                       } else {
-                               posn = &(*posn)->next;
-                       }
+               item = string_list_lookup(ref_map->peer_ref->name, &refs);
+               if (item) {
+                       if (strcmp(((struct ref *)item->util)->name,
+                                  ref_map->name))
+                               die("%s tracks both %s and %s",
+                                   ref_map->peer_ref->name,
+                                   ((struct ref *)item->util)->name,
+                                   ref_map->name);
+                       prev->next = ref_map->next;
+                       free(ref_map->peer_ref);
+                       free(ref_map);
+                       ref_map = prev; /* skip this; we freed it */
+                       continue;
                }
+               item = string_list_insert(ref_map->peer_ref->name, &refs);
+               item->util = ref_map;
        }
+       string_list_clear(&refs, 0);
  }
  
  int remote_has_url(struct remote *remote, const char *url)
@@@ -821,23 -810,6 +827,23 @@@ static int match_name_with_pattern(cons
        return ret;
  }
  
 +char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
 +                   const char *name)
 +{
 +      int i;
 +      char *ret = NULL;
 +      for (i = 0; i < nr_refspec; i++) {
 +              struct refspec *refspec = refspecs + i;
 +              if (refspec->pattern) {
 +                      if (match_name_with_pattern(refspec->src, name,
 +                                                  refspec->dst, &ret))
 +                              return ret;
 +              } else if (!strcmp(refspec->src, name))
 +                      return strdup(refspec->dst);
 +      }
 +      return NULL;
 +}
 +
  int remote_find_tracking(struct remote *remote, struct refspec *refspec)
  {
        int find_src = refspec->src == NULL;
@@@ -1620,3 -1592,42 +1626,42 @@@ struct ref *guess_remote_head(const str
  
        return list;
  }
+ struct stale_heads_info {
+       struct remote *remote;
+       struct string_list *ref_names;
+       struct ref **stale_refs_tail;
+ };
+ static int get_stale_heads_cb(const char *refname,
+       const unsigned char *sha1, int flags, void *cb_data)
+ {
+       struct stale_heads_info *info = cb_data;
+       struct refspec refspec;
+       memset(&refspec, 0, sizeof(refspec));
+       refspec.dst = (char *)refname;
+       if (!remote_find_tracking(info->remote, &refspec)) {
+               if (!((flags & REF_ISSYMREF) ||
+                   string_list_has_string(info->ref_names, refspec.src))) {
+                       struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
+                       hashcpy(ref->new_sha1, sha1);
+               }
+       }
+       return 0;
+ }
+ struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map)
+ {
+       struct ref *ref, *stale_refs = NULL;
+       struct string_list ref_names = { NULL, 0, 0, 0 };
+       struct stale_heads_info info;
+       info.remote = remote;
+       info.ref_names = &ref_names;
+       info.stale_refs_tail = &stale_refs;
+       for (ref = fetch_map; ref; ref = ref->next)
+               string_list_append(ref->name, &ref_names);
+       sort_string_list(&ref_names);
+       for_each_ref(get_stale_heads_cb, &info);
+       string_list_clear(&ref_names, 0);
+       return stale_refs;
+ }
diff --combined remote.h
index cdc3b5b159351b550f7136594e0d9f930177e576,d0aba81ead1847e43a971362659abf1c1737c12f..8b7ecf9197f2a9210299c9700cc88a92a796f714
+++ b/remote.h
@@@ -11,8 -11,6 +11,8 @@@ struct remote 
        const char *name;
        int origin;
  
 +      const char *foreign_vcs;
 +
        const char **url;
        int url_nr;
        int url_alloc;
@@@ -91,11 -89,6 +91,11 @@@ void ref_remove_duplicates(struct ref *
  int valid_fetch_refspec(const char *refspec);
  struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
  
 +void free_refspec(int nr_refspec, struct refspec *refspec);
 +
 +char *apply_refspecs(struct refspec *refspecs, int nr_refspec,
 +                   const char *name);
 +
  int match_refs(struct ref *src, struct ref **dst,
               int nr_refspec, const char **refspec, int all);
  
@@@ -161,4 -154,7 +161,7 @@@ struct ref *guess_remote_head(const str
                              const struct ref *refs,
                              int all);
  
+ /* Return refs which no longer exist on remote */
+ struct ref *get_stale_heads(struct remote *remote, struct ref *fetch_map);
  #endif
diff --combined t/test-lib.sh
index 6ed5b28be55a9c7ddb85e21dcb6a538a766f5671,ec3336aba5a65a468bc6ce71f33a9cca76dbfe0f..4a40520595e9d932640dd603b28c4472c945e63b
@@@ -30,7 -30,7 +30,7 @@@ TZ=UT
  TERM=dumb
  export LANG LC_ALL PAGER TERM TZ
  EDITOR=:
- VISUAL=:
+ unset VISUAL
  unset GIT_EDITOR
  unset AUTHOR_DATE
  unset AUTHOR_EMAIL
@@@ -58,7 -58,7 +58,7 @@@ GIT_MERGE_VERBOSITY=
  export GIT_MERGE_VERBOSITY
  export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME
  export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME
- export EDITOR VISUAL
+ export EDITOR
  GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
  
  # Protect ourselves from common misconfiguration to export
@@@ -207,8 -207,8 +207,8 @@@ trap 'die' EXI
  test_set_editor () {
        FAKE_EDITOR="$1"
        export FAKE_EDITOR
-       VISUAL='"$FAKE_EDITOR"'
-       export VISUAL
+       EDITOR='"$FAKE_EDITOR"'
+       export EDITOR
  }
  
  test_tick () {
@@@ -638,15 -638,6 +638,15 @@@ test -d ../templates/blt || 
        error "You haven't built things yet, have you?"
  }
  
 +if test -z "$GIT_TEST_INSTALLED"
 +then
 +      GITPYTHONLIB="$(pwd)/../git_remote_helpers/build/lib"
 +      export GITPYTHONLIB
 +      test -d ../git_remote_helpers/build || {
 +              error "You haven't built git_remote_helpers yet, have you?"
 +      }
 +fi
 +
  if ! test -x ../test-chmtime; then
        echo >&2 'You need to build test-chmtime:'
        echo >&2 'Run "make test-chmtime" in the source (toplevel) directory'
@@@ -738,7 -729,6 +738,7 @@@ case $(uname -s) i
  esac
  
  test -z "$NO_PERL" && test_set_prereq PERL
 +test -z "$NO_PYTHON" && test_set_prereq PYTHON
  
  # test whether the filesystem supports symbolic links
  ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
diff --combined transport-helper.c
index c87530e87d0891a9e14c8fcddd57051b0fe1bcde,5078c7100f16e9d7ac08aad05e3797ad5b6626bc..11f3d7ec52893dcdee127d8493f1897c6c091b31
@@@ -1,21 -1,20 +1,25 @@@
  #include "cache.h"
  #include "transport.h"
+ #include "quote.h"
  #include "run-command.h"
  #include "commit.h"
  #include "diff.h"
  #include "revision.h"
+ #include "quote.h"
 +#include "remote.h"
  
  struct helper_data
  {
        const char *name;
        struct child_process *helper;
-       unsigned fetch : 1;
-       unsigned import : 1;
+       FILE *out;
+       unsigned fetch : 1,
++              import : 1,
+               option : 1,
+               push : 1;
 +      /* These go from remote name (as in "list") to private name */
 +      struct refspec *refspecs;
 +      int refspec_nr;
  };
  
  static struct child_process *get_helper(struct transport *transport)
        struct helper_data *data = transport->data;
        struct strbuf buf = STRBUF_INIT;
        struct child_process *helper;
-       FILE *file;
 +      const char **refspecs = NULL;
 +      int refspec_nr = 0;
 +      int refspec_alloc = 0;
  
        if (data->helper)
                return data->helper;
  
        write_str_in_full(helper->in, "capabilities\n");
  
-       file = xfdopen(helper->out, "r");
+       data->out = xfdopen(helper->out, "r");
        while (1) {
-               if (strbuf_getline(&buf, file, '\n') == EOF)
+               if (strbuf_getline(&buf, data->out, '\n') == EOF)
                        exit(128); /* child died, message supplied already */
  
                if (!*buf.buf)
                        break;
                if (!strcmp(buf.buf, "fetch"))
                        data->fetch = 1;
+               if (!strcmp(buf.buf, "option"))
+                       data->option = 1;
+               if (!strcmp(buf.buf, "push"))
+                       data->push = 1;
 +              if (!strcmp(buf.buf, "import"))
 +                      data->import = 1;
 +              if (!data->refspecs && !prefixcmp(buf.buf, "refspec ")) {
 +                      ALLOC_GROW(refspecs,
 +                                 refspec_nr + 1,
 +                                 refspec_alloc);
 +                      refspecs[refspec_nr++] = strdup(buf.buf + strlen("refspec "));
 +              }
 +      }
 +      if (refspecs) {
 +              int i;
 +              data->refspec_nr = refspec_nr;
 +              data->refspecs = parse_fetch_refspec(refspec_nr, refspecs);
 +              for (i = 0; i < refspec_nr; i++) {
 +                      free((char *)refspecs[i]);
 +              }
 +              free(refspecs);
        }
 +      strbuf_release(&buf);
        return data->helper;
  }
  
@@@ -84,33 -65,104 +91,113 @@@ static int disconnect_helper(struct tra
        if (data->helper) {
                write_str_in_full(data->helper->in, "\n");
                close(data->helper->in);
+               fclose(data->out);
                finish_command(data->helper);
                free((char *)data->helper->argv[0]);
                free(data->helper->argv);
                free(data->helper);
                data->helper = NULL;
        }
 -      free(data);
        return 0;
  }
  
+ static const char *unsupported_options[] = {
+       TRANS_OPT_UPLOADPACK,
+       TRANS_OPT_RECEIVEPACK,
+       TRANS_OPT_THIN,
+       TRANS_OPT_KEEP
+       };
+ static const char *boolean_options[] = {
+       TRANS_OPT_THIN,
+       TRANS_OPT_KEEP,
+       TRANS_OPT_FOLLOWTAGS
+       };
+ 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;
+       if (!data->option)
+               return 1;
+       for (i = 0; i < ARRAY_SIZE(unsupported_options); i++) {
+               if (!strcmp(name, unsupported_options[i]))
+                       return 1;
+       }
+       for (i = 0; i < ARRAY_SIZE(boolean_options); i++) {
+               if (!strcmp(name, boolean_options[i])) {
+                       is_bool = 1;
+                       break;
+               }
+       }
+       strbuf_addf(&buf, "option %s ", name);
+       if (is_bool)
+               strbuf_addstr(&buf, value ? "true" : "false");
+       else
+               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 */
+       if (!strcmp(buf.buf, "ok"))
+               ret = 0;
+       else if (!prefixcmp(buf.buf, "error")) {
+               ret = -1;
+       } else if (!strcmp(buf.buf, "unsupported"))
+               ret = 1;
+       else {
+               warning("%s unexpectedly said: '%s'", data->name, buf.buf);
+               ret = 1;
+       }
+       strbuf_release(&buf);
+       return ret;
+ }
+ 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));
+       set_helper_option(t, "progress", !no_progress ? "true" : "false");
+       n = snprintf(buf, sizeof(buf), "%d", v + 1);
+       if (n >= sizeof(buf))
+               die("impossibly large verbosity value");
+       set_helper_option(t, "verbosity", buf);
+ }
 +static int release_helper(struct transport *transport)
 +{
 +      struct helper_data *data = transport->data;
 +      free_refspec(data->refspec_nr, data->refspecs);
 +      data->refspecs = NULL;
 +      disconnect_helper(transport);
 +      free(transport->data);
 +      return 0;
 +}
 +
  static int fetch_with_fetch(struct transport *transport,
 -                          int nr_heads, const struct ref **to_fetch)
 +                          int nr_heads, struct ref **to_fetch)
  {
-       struct child_process *helper = get_helper(transport);
-       FILE *file = xfdopen(helper->out, "r");
+       struct helper_data *data = transport->data;
        int i;
        struct strbuf buf = STRBUF_INIT;
  
+       standard_options(transport);
        for (i = 0; i < nr_heads; i++) {
                const struct ref *posn = to_fetch[i];
                if (posn->status & REF_STATUS_UPTODATE)
  
                strbuf_addf(&buf, "fetch %s %s\n",
                            sha1_to_hex(posn->old_sha1), posn->name);
-               write_in_full(helper->in, buf.buf, buf.len);
-               strbuf_reset(&buf);
+       }
  
-               if (strbuf_getline(&buf, file, '\n') == EOF)
+       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);
+       while (1) {
+               strbuf_reset(&buf);
+               if (strbuf_getline(&buf, data->out, '\n') == EOF)
                        exit(128); /* child died, message supplied already */
+               if (!prefixcmp(buf.buf, "lock ")) {
+                       const char *name = buf.buf + 5;
+                       if (transport->pack_lockfile)
+                               warning("%s also locked %s", data->name, name);
+                       else
+                               transport->pack_lockfile = xstrdup(name);
+               }
+               else if (!buf.len)
+                       break;
+               else
+                       warning("%s unexpectedly said: '%s'", data->name, buf.buf);
        }
+       strbuf_release(&buf);
        return 0;
  }
  
 +static int get_importer(struct transport *transport, struct child_process *fastimport)
 +{
 +      struct child_process *helper = get_helper(transport);
 +      memset(fastimport, 0, sizeof(*fastimport));
 +      fastimport->in = helper->out;
 +      fastimport->argv = xcalloc(5, sizeof(*fastimport->argv));
 +      fastimport->argv[0] = "fast-import";
 +      fastimport->argv[1] = "--quiet";
 +
 +      fastimport->git_cmd = 1;
 +      return start_command(fastimport);
 +}
 +
 +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;
 +
 +      if (get_importer(transport, &fastimport))
 +              die("Couldn't run fast-import");
 +
 +      for (i = 0; i < nr_heads; i++) {
 +              posn = to_fetch[i];
 +              if (posn->status & REF_STATUS_UPTODATE)
 +                      continue;
 +
 +              strbuf_addf(&buf, "import %s\n", posn->name);
 +              write_in_full(helper->in, buf.buf, buf.len);
 +              strbuf_reset(&buf);
 +      }
 +      disconnect_helper(transport);
 +      finish_command(&fastimport);
 +      free(fastimport.argv);
 +      fastimport.argv = NULL;
 +
 +      for (i = 0; i < nr_heads; i++) {
 +              char *private;
 +              posn = to_fetch[i];
 +              if (posn->status & REF_STATUS_UPTODATE)
 +                      continue;
 +              if (data->refspecs)
 +                      private = apply_refspecs(data->refspecs, data->refspec_nr, posn->name);
 +              else
 +                      private = strdup(posn->name);
 +              read_ref(private, posn->old_sha1);
 +              free(private);
 +      }
 +      strbuf_release(&buf);
 +      return 0;
 +}
 +
  static int fetch(struct transport *transport,
 -               int nr_heads, const struct ref **to_fetch)
 +               int nr_heads, struct ref **to_fetch)
  {
        struct helper_data *data = transport->data;
        int i, count;
        if (data->fetch)
                return fetch_with_fetch(transport, nr_heads, to_fetch);
  
 +      if (data->import)
 +              return fetch_with_import(transport, nr_heads, to_fetch);
 +
        return -1;
  }
  
+ static int push_refs(struct transport *transport,
+               struct ref *remote_refs, int flags)
+ {
+       int force_all = flags & TRANSPORT_PUSH_FORCE;
+       int mirror = flags & TRANSPORT_PUSH_MIRROR;
+       struct helper_data *data = transport->data;
+       struct strbuf buf = STRBUF_INIT;
+       struct child_process *helper;
+       struct ref *ref;
+       if (!remote_refs)
+               return 0;
+       helper = get_helper(transport);
+       if (!data->push)
+               return 1;
+       for (ref = remote_refs; ref; ref = ref->next) {
+               if (ref->peer_ref)
+                       hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+               else if (!mirror)
+                       continue;
+               ref->deletion = is_null_sha1(ref->new_sha1);
+               if (!ref->deletion &&
+                       !hashcmp(ref->old_sha1, ref->new_sha1)) {
+                       ref->status = REF_STATUS_UPTODATE;
+                       continue;
+               }
+               if (force_all)
+                       ref->force = 1;
+               strbuf_addstr(&buf, "push ");
+               if (!ref->deletion) {
+                       if (ref->force)
+                               strbuf_addch(&buf, '+');
+                       if (ref->peer_ref)
+                               strbuf_addstr(&buf, ref->peer_ref->name);
+                       else
+                               strbuf_addstr(&buf, sha1_to_hex(ref->new_sha1));
+               }
+               strbuf_addch(&buf, ':');
+               strbuf_addstr(&buf, ref->name);
+               strbuf_addch(&buf, '\n');
+       }
+       if (buf.len == 0)
+               return 0;
+       transport->verbose = flags & TRANSPORT_PUSH_VERBOSE ? 1 : 0;
+       standard_options(transport);
+       if (flags & TRANSPORT_PUSH_DRY_RUN) {
+               if (set_helper_option(transport, "dry-run", "true") != 0)
+                       die("helper %s does not support dry-run", data->name);
+       }
+       strbuf_addch(&buf, '\n');
+       if (write_in_full(helper->in, buf.buf, buf.len) != buf.len)
+               exit(128);
+       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 */
+               if (!buf.len)
+                       break;
+               if (!prefixcmp(buf.buf, "ok ")) {
+                       status = REF_STATUS_OK;
+                       refname = buf.buf + 3;
+               } else if (!prefixcmp(buf.buf, "error ")) {
+                       status = REF_STATUS_REMOTE_REJECT;
+                       refname = buf.buf + 6;
+               } else
+                       die("expected ok/error, helper said '%s'\n", buf.buf);
+               msg = strchr(refname, ' ');
+               if (msg) {
+                       struct strbuf msg_buf = STRBUF_INIT;
+                       const char *end;
+                       *msg++ = '\0';
+                       if (!unquote_c_style(&msg_buf, msg, &end))
+                               msg = strbuf_detach(&msg_buf, NULL);
+                       else
+                               msg = xstrdup(msg);
+                       strbuf_release(&msg_buf);
+                       if (!strcmp(msg, "no match")) {
+                               status = REF_STATUS_NONE;
+                               free(msg);
+                               msg = NULL;
+                       }
+                       else if (!strcmp(msg, "up to date")) {
+                               status = REF_STATUS_UPTODATE;
+                               free(msg);
+                               msg = NULL;
+                       }
+                       else if (!strcmp(msg, "non-fast forward")) {
+                               status = REF_STATUS_REJECT_NONFASTFORWARD;
+                               free(msg);
+                               msg = NULL;
+                       }
+               }
+               if (ref)
+                       ref = find_ref_by_name(ref, refname);
+               if (!ref)
+                       ref = find_ref_by_name(remote_refs, refname);
+               if (!ref) {
+                       warning("helper reported unexpected status of %s", refname);
+                       continue;
+               }
+               ref->status = status;
+               ref->remote_status = msg;
+       }
+       strbuf_release(&buf);
+       return 0;
+ }
 +static int has_attribute(const char *attrs, const char *attr) {
 +      int len;
 +      if (!attrs)
 +              return 0;
 +
 +      len = strlen(attr);
 +      for (;;) {
 +              const char *space = strchrnul(attrs, ' ');
 +              if (len == space - attrs && !strncmp(attrs, attr, len))
 +                      return 1;
 +              if (!*space)
 +                      return 0;
 +              attrs = space + 1;
 +      }
 +}
 +
  static struct ref *get_refs_list(struct transport *transport, int for_push)
  {
+       struct helper_data *data = transport->data;
        struct child_process *helper;
        struct ref *ret = NULL;
        struct ref **tail = &ret;
        struct ref *posn;
        struct strbuf buf = STRBUF_INIT;
-       FILE *file;
  
        helper = get_helper(transport);
  
-       write_str_in_full(helper->in, "list\n");
+       if (data->push && for_push)
+               write_str_in_full(helper->in, "list for-push\n");
+       else
+               write_str_in_full(helper->in, "list\n");
  
-       file = xfdopen(helper->out, "r");
        while (1) {
                char *eov, *eon;
-               if (strbuf_getline(&buf, file, '\n') == EOF)
+               if (strbuf_getline(&buf, data->out, '\n') == EOF)
                        exit(128); /* child died, message supplied already */
  
                if (!*buf.buf)
                        (*tail)->symref = xstrdup(buf.buf + 1);
                else if (buf.buf[0] != '?')
                        get_sha1_hex(buf.buf, (*tail)->old_sha1);
 +              if (eon) {
 +                      if (has_attribute(eon + 1, "unchanged")) {
 +                              (*tail)->status |= REF_STATUS_UPTODATE;
 +                              read_ref((*tail)->name, (*tail)->old_sha1);
 +                      }
 +              }
                tail = &((*tail)->next);
        }
        strbuf_release(&buf);
@@@ -278,8 -395,10 +511,10 @@@ int transport_helper_init(struct transp
        data->name = name;
  
        transport->data = data;
+       transport->set_option = set_helper_option;
        transport->get_refs_list = get_refs_list;
        transport->fetch = fetch;
 -      transport->disconnect = disconnect_helper;
+       transport->push_refs = push_refs;
 +      transport->disconnect = release_helper;
        return 0;
  }
diff --combined transport.c
index 5d814b50e4c1b52df4b1ab2c5fbebaeedc16e634,7362ec09b2cbc6752489286a8280c16d3519f163..3eea836a33a56aaa99eec78bb4850b02888e377b
@@@ -204,7 -204,7 +204,7 @@@ static struct ref *get_refs_via_rsync(s
  }
  
  static int fetch_objs_via_rsync(struct transport *transport,
 -                              int nr_objs, const struct ref **to_fetch)
 +                              int nr_objs, struct ref **to_fetch)
  {
        struct strbuf buf = STRBUF_INIT;
        struct child_process rsync;
@@@ -349,35 -349,6 +349,6 @@@ static int rsync_transport_push(struct 
        return result;
  }
  
- #ifndef NO_CURL
- static int curl_transport_push(struct transport *transport, int refspec_nr, const char **refspec, int flags)
- {
-       const char **argv;
-       int argc;
-       if (flags & TRANSPORT_PUSH_MIRROR)
-               return error("http transport does not support mirror mode");
-       argv = xmalloc((refspec_nr + 12) * sizeof(char *));
-       argv[0] = "http-push";
-       argc = 1;
-       if (flags & TRANSPORT_PUSH_ALL)
-               argv[argc++] = "--all";
-       if (flags & TRANSPORT_PUSH_FORCE)
-               argv[argc++] = "--force";
-       if (flags & TRANSPORT_PUSH_DRY_RUN)
-               argv[argc++] = "--dry-run";
-       if (flags & TRANSPORT_PUSH_VERBOSE)
-               argv[argc++] = "--verbose";
-       argv[argc++] = transport->url;
-       while (refspec_nr--)
-               argv[argc++] = *refspec++;
-       argv[argc] = NULL;
-       return !!run_command_v_opt(argv, RUN_GIT_CMD);
- }
- #endif
  struct bundle_transport_data {
        int fd;
        struct bundle_header header;
@@@ -408,7 -379,7 +379,7 @@@ static struct ref *get_refs_from_bundle
  }
  
  static int fetch_refs_from_bundle(struct transport *transport,
 -                             int nr_heads, const struct ref **to_fetch)
 +                             int nr_heads, struct ref **to_fetch)
  {
        struct bundle_transport_data *data = transport->data;
        return unbundle(&data->header, data->fd);
@@@ -486,7 -457,7 +457,7 @@@ static struct ref *get_refs_via_connect
  }
  
  static int fetch_refs_via_pack(struct transport *transport,
 -                             int nr_heads, const struct ref **to_fetch)
 +                             int nr_heads, struct ref **to_fetch)
  {
        struct git_transport_data *data = transport->data;
        char **heads = xmalloc(nr_heads * sizeof(*heads));
@@@ -668,7 -639,7 +639,7 @@@ static int print_one_push_status(struc
                break;
        case REF_STATUS_REJECT_NONFASTFORWARD:
                print_ref_status('!', "[rejected]", ref, ref->peer_ref,
-                                                "non-fast forward", porcelain);
+                                                "non-fast-forward", porcelain);
                break;
        case REF_STATUS_REMOTE_REJECT:
                print_ref_status('!', "[remote rejected]", ref,
@@@ -760,6 -731,7 +731,7 @@@ static int git_transport_push(struct tr
                                 NULL);
        }
  
+       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;
@@@ -812,27 -784,12 +784,30 @@@ struct transport *transport_get(struct 
  {
        struct transport *ret = xcalloc(1, sizeof(*ret));
  
+       if (!remote)
+               die("No remote provided to transport_get()");
        ret->remote = remote;
 +
 +      if (!url && remote && remote->url)
 +              url = remote->url[0];
        ret->url = url;
  
 +      /* maybe it is a foreign URL? */
 +      if (url) {
 +              const char *p = url;
 +
 +              while (isalnum(*p))
 +                      p++;
 +              if (!prefixcmp(p, "::"))
 +                      remote->foreign_vcs = xstrndup(url, p - url);
 +      }
 +
 +      if (remote && remote->foreign_vcs) {
 +              transport_helper_init(ret, remote->foreign_vcs);
 +              return ret;
 +      }
 +
        if (!prefixcmp(url, "rsync:")) {
                ret->get_refs_list = get_refs_via_rsync;
                ret->fetch = fetch_objs_via_rsync;
                transport_helper_init(ret, "curl");
  #ifdef NO_CURL
                error("git was compiled without libcurl support.");
- #else
-               ret->push = curl_transport_push;
  #endif
  
        } else if (is_local(url) && is_file(url)) {
                data->thin = 1;
                data->conn = NULL;
                data->uploadpack = "git-upload-pack";
-               if (remote && remote->uploadpack)
+               if (remote->uploadpack)
                        data->uploadpack = remote->uploadpack;
                data->receivepack = "git-receive-pack";
-               if (remote && remote->receivepack)
+               if (remote->receivepack)
                        data->receivepack = remote->receivepack;
        }
  
@@@ -941,17 -896,16 +914,17 @@@ const struct ref *transport_get_remote_
        return transport->remote_refs;
  }
  
 -int transport_fetch_refs(struct transport *transport, const struct ref *refs)
 +int transport_fetch_refs(struct transport *transport, struct ref *refs)
  {
        int rc;
        int nr_heads = 0, nr_alloc = 0, nr_refs = 0;
 -      const struct ref **heads = NULL;
 -      const struct ref *rm;
 +      struct ref **heads = NULL;
 +      struct ref *rm;
  
        for (rm = refs; rm; rm = rm->next) {
                nr_refs++;
                if (rm->peer_ref &&
 +                  !is_null_sha1(rm->old_sha1) &&
                    !hashcmp(rm->peer_ref->old_sha1, rm->old_sha1))
                        continue;
                ALLOC_GROW(heads, nr_heads + 1, nr_alloc);
diff --combined transport.h
index 503db11701b44766716bbe435e58a69d0d36b871,e4e6177e2632b4a764703c11ef0a8033eafea037..9e74406fa203a59920cb24884d7893b45d876a70
@@@ -18,51 -18,14 +18,51 @@@ struct transport 
        int (*set_option)(struct transport *connection, const char *name,
                          const char *value);
  
 +      /**
 +       * Returns a list of the remote side's refs. In order to allow
 +       * the transport to try to share connections, for_push is a
 +       * hint as to whether the ultimate operation is a push or a fetch.
 +       *
 +       * If the transport is able to determine the remote hash for
 +       * the ref without a huge amount of effort, it should store it
 +       * in the ref's old_sha1 field; otherwise it should be all 0.
 +       **/
        struct ref *(*get_refs_list)(struct transport *transport, int for_push);
 -      int (*fetch)(struct transport *transport, int refs_nr, const struct ref **refs);
 +
 +      /**
 +       * Fetch the objects for the given refs. Note that this gets
 +       * an array, and should ignore the list structure.
 +       *
 +       * If the transport did not get hashes for refs in
 +       * get_refs_list(), it should set the old_sha1 fields in the
 +       * provided refs now.
 +       **/
 +      int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);
 +
 +      /**
 +       * Push the objects and refs. Send the necessary objects, and
 +       * then, for any refs where peer_ref is set and
 +       * peer_ref->new_sha1 is different from old_sha1, tell the
 +       * remote side to update each ref in the list from old_sha1 to
 +       * peer_ref->new_sha1.
 +       *
 +       * Where possible, set the status for each ref appropriately.
 +       *
 +       * The transport must modify new_sha1 in the ref to the new
 +       * value if the remote accepted the change. Note that this
 +       * could be a different value from peer_ref->new_sha1 if the
 +       * process involved generating new commits.
 +       **/
        int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
        int (*push)(struct transport *connection, int refspec_nr, const char **refspec, int flags);
  
 +      /** get_refs_list(), fetch(), and push_refs() can keep
 +       * resources (such as a connection) reserved for futher
 +       * use. disconnect() releases these resources.
 +       **/
        int (*disconnect)(struct transport *connection);
        char *pack_lockfile;
-       signed verbose : 2;
+       signed verbose : 3;
        /* Force progress even if the output is not a tty */
        unsigned progress : 1;
  };
@@@ -111,7 -74,7 +111,7 @@@ int transport_push(struct transport *co
  
  const struct ref *transport_get_remote_refs(struct transport *transport);
  
 -int transport_fetch_refs(struct transport *transport, const struct ref *refs);
 +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);