Merge branch 'bw/protocol-v1'
authorJunio C Hamano <gitster@pobox.com>
Wed, 6 Dec 2017 17:23:44 +0000 (09:23 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 6 Dec 2017 17:23:44 +0000 (09:23 -0800)
A new mechanism to upgrade the wire protocol in place is proposed
and demonstrated that it works with the older versions of Git
without harming them.

* bw/protocol-v1:
Documentation: document Extra Parameters
ssh: introduce a 'simple' ssh variant
i5700: add interop test for protocol transition
http: tell server that the client understands v1
connect: tell server that the client understands v1
connect: teach client to recognize v1 server response
upload-pack, receive-pack: introduce protocol version 1
daemon: recognize hidden request arguments
protocol: introduce protocol extension mechanisms
pkt-line: add packet_write function
connect: in ref advertisement, shallows are last

1  2 
Documentation/config.txt
Documentation/git.txt
Makefile
builtin/receive-pack.c
cache.h
http.c
pkt-line.c
t/t5601-clone.sh
upload-pack.c
diff --combined Documentation/config.txt
index 531649cb40eab1749928bfacce8fa2ec0f24d065,0460af37e2617d0ab7ab8f33c3d9d745237da606..64bdce84356291a8c6eec7f37b75b53f7d548225
@@@ -351,9 -351,6 +351,9 @@@ advice.*:
        addEmbeddedRepo::
                Advice on what to do when you've accidentally added one
                git repo inside of another.
 +      ignoredHook::
 +              Advice shown if an hook is ignored because the hook is not
 +              set as executable.
  --
  
  core.fileMode::
@@@ -416,13 -413,6 +416,13 @@@ core.protectNTFS:
        8.3 "short" names.
        Defaults to `true` on Windows, and `false` elsewhere.
  
 +core.fsmonitor::
 +      If set, the value of this variable is used as a command which
 +      will identify all files that may have changed since the
 +      requested date/time. This information is used to speed up git by
 +      avoiding unnecessary processing of files that have not changed.
 +      See the "fsmonitor-watchman" section of linkgit:githooks[5].
 +
  core.trustctime::
        If false, the ctime differences between the index and the
        working tree are ignored; useful when the inode change time
@@@ -959,23 -949,6 +959,23 @@@ apply.whitespace:
        Tells 'git apply' how to handle whitespaces, in the same way
        as the `--whitespace` option. See linkgit:git-apply[1].
  
 +blame.showRoot::
 +      Do not treat root commits as boundaries in linkgit:git-blame[1].
 +      This option defaults to false.
 +
 +blame.blankBoundary::
 +      Show blank commit object name for boundary commits in
 +      linkgit:git-blame[1]. This option defaults to false.
 +
 +blame.showEmail::
 +      Show the author email instead of author name in linkgit:git-blame[1].
 +      This option defaults to false.
 +
 +blame.date::
 +      Specifies the format used to output dates in linkgit:git-blame[1].
 +      If unset the iso format is used. For supported values,
 +      see the discussion of the `--date` option at linkgit:git-log[1].
 +
  branch.autoSetupMerge::
        Tells 'git branch' and 'git checkout' to set up new branches
        so that linkgit:git-pull[1] will appropriately merge from the
@@@ -2111,12 -2084,31 +2111,31 @@@ ssh.variant:
        Depending on the value of the environment variables `GIT_SSH` or
        `GIT_SSH_COMMAND`, or the config setting `core.sshCommand`, Git
        auto-detects whether to adjust its command-line parameters for use
-       with plink or tortoiseplink, as opposed to the default (OpenSSH).
+       with ssh (OpenSSH), plink or tortoiseplink, as opposed to the default
+       (simple).
  +
  The config variable `ssh.variant` can be set to override this auto-detection;
- valid values are `ssh`, `plink`, `putty` or `tortoiseplink`. Any other value
- will be treated as normal ssh. This setting can be overridden via the
- environment variable `GIT_SSH_VARIANT`.
+ valid values are `ssh`, `simple`, `plink`, `putty` or `tortoiseplink`. Any
+ other value will be treated as normal ssh. This setting can be overridden via
+ the environment variable `GIT_SSH_VARIANT`.
+ +
+ The current command-line parameters used for each variant are as
+ follows:
+ +
+ --
+ * `ssh` - [-p port] [-4] [-6] [-o option] [username@]host command
+ * `simple` - [username@]host command
+ * `plink` or `putty` - [-P port] [-4] [-6] [username@]host command
+ * `tortoiseplink` - [-P port] [-4] [-6] -batch [username@]host command
+ --
+ +
+ Except for the `simple` variant, command-line parameters are likely to
+ change as git gains new features.
  
  i18n.commitEncoding::
        Character encoding the commit messages are stored in; Git itself
@@@ -2544,6 -2536,23 +2563,23 @@@ The protocol names currently used by gi
      `hg` to allow the `git-remote-hg` helper)
  --
  
+ protocol.version::
+       Experimental. If set, clients will attempt to communicate with a
+       server using the specified protocol version.  If unset, no
+       attempt will be made by the client to communicate using a
+       particular protocol version, this results in protocol version 0
+       being used.
+       Supported versions:
+ +
+ --
+ * `0` - the original wire protocol.
+ * `1` - the original wire protocol with the addition of a version string
+   in the initial response from the server.
+ --
  pull.ff::
        By default, Git does not create an extra merge commit when merging
        a commit that is a descendant of the current commit. Instead, the
@@@ -2648,35 -2657,6 +2684,35 @@@ push.gpgSign:
        override a value from a lower-priority config file. An explicit
        command-line flag always overrides this config option.
  
 +push.pushOption::
 +      When no `--push-option=<option>` argument is given from the
 +      command line, `git push` behaves as if each <value> of
 +      this variable is given as `--push-option=<value>`.
 ++
 +This is a multi-valued variable, and an empty value can be used in a
 +higher priority configuration file (e.g. `.git/config` in a
 +repository) to clear the values inherited from a lower priority
 +configuration files (e.g. `$HOME/.gitconfig`).
 ++
 +--
 +
 +Example:
 +
 +/etc/gitconfig
 +  push.pushoption = a
 +  push.pushoption = b
 +
 +~/.gitconfig
 +  push.pushoption = c
 +
 +repo/.git/config
 +  push.pushoption =
 +  push.pushoption = b
 +
 +This will result in only b (a and c are cleared).
 +
 +--
 +
  push.recurseSubmodules::
        Make sure all submodule commits used by the revisions to be pushed
        are available on a remote-tracking branch. If the value is 'check'
@@@ -3007,7 -2987,6 +3043,7 @@@ sendemail.smtpPass:
  sendemail.suppresscc::
  sendemail.suppressFrom::
  sendemail.to::
 +sendemail.tocmd::
  sendemail.smtpDomain::
  sendemail.smtpServer::
  sendemail.smtpServerPort::
@@@ -3142,14 -3121,10 +3178,14 @@@ submodule.<name>.url:
        See linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
  
  submodule.<name>.update::
 -      The default update procedure for a submodule. This variable
 -      is populated by `git submodule init` from the
 -      linkgit:gitmodules[5] file. See description of 'update'
 -      command in linkgit:git-submodule[1].
 +      The method by which a submodule is updated by 'git submodule update',
 +      which is the only affected command, others such as
 +      'git checkout --recurse-submodules' are unaffected. It exists for
 +      historical reasons, when 'git submodule' was the only command to
 +      interact with submodules; settings like `submodule.active`
 +      and `pull.rebase` are more specific. It is populated by
 +      `git submodule init` from the linkgit:gitmodules[5] file.
 +      See description of 'update' command in linkgit:git-submodule[1].
  
  submodule.<name>.branch::
        The remote branch name for a submodule, used by `git submodule
diff --combined Documentation/git.txt
index 483a1f35475ea435e5c93121e67ad8085855bfc3,8bc3f21473b339983c6b4456a83a8393e7b15616..e75db104e3a33dde93a98cc09de97f4d4f0bc5eb
@@@ -75,7 -75,7 +75,7 @@@ example the following invocations are e
  Note that omitting the `=` in `git -c foo.bar ...` is allowed and sets
  `foo.bar` to the boolean true value (just like `[foo]bar` would in a
  config file). Including the equals but with an empty value (like `git -c
 -foo.bar= ...`) sets `foo.bar` to the empty string which ` git config
 +foo.bar= ...`) sets `foo.bar` to the empty string which `git config
  --bool` will convert to `false`.
  
  --exec-path[=<path>]::
        Add "icase" magic to all pathspec. This is equivalent to setting
        the `GIT_ICASE_PATHSPECS` environment variable to `1`.
  
 +--no-optional-locks::
 +      Do not perform optional operations that require locks. This is
 +      equivalent to setting the `GIT_OPTIONAL_LOCKS` to `0`.
 +
  GIT COMMANDS
  ------------
  
@@@ -522,11 -518,10 +522,10 @@@ othe
        If either of these environment variables is set then 'git fetch'
        and 'git push' will use the specified command instead of 'ssh'
        when they need to connect to a remote system.
-       The command will be given exactly two or four arguments: the
-       'username@host' (or just 'host') from the URL and the shell
-       command to execute on that remote system, optionally preceded by
-       `-p` (literally) and the 'port' from the URL when it specifies
-       something other than the default SSH port.
+       The command-line parameters passed to the configured command are
+       determined by the ssh variant.  See `ssh.variant` option in
+       linkgit:git-config[1] for details.
  +
  `$GIT_SSH_COMMAND` takes precedence over `$GIT_SSH`, and is interpreted
  by the shell, which allows additional arguments to be included.
@@@ -595,10 -590,6 +594,10 @@@ into it
  Unsetting the variable, or setting it to empty, "0" or
  "false" (case insensitive) disables trace messages.
  
 +`GIT_TRACE_FSMONITOR`::
 +      Enables trace messages for the filesystem monitor extension.
 +      See `GIT_TRACE` for available trace output options.
 +
  `GIT_TRACE_PACK_ACCESS`::
        Enables trace messages for all accesses to any packs. For each
        access, the pack file name and an offset in the pack is
@@@ -705,32 -696,12 +704,38 @@@ of clones and fetches
        which feed potentially-untrusted URLS to git commands.  See
        linkgit:git-config[1] for more details.
  
+ `GIT_PROTOCOL`::
+       For internal use only.  Used in handshaking the wire protocol.
+       Contains a colon ':' separated list of keys with optional values
+       'key[=value]'.  Presence of unknown keys and values must be
+       ignored.
 +`GIT_OPTIONAL_LOCKS`::
 +      If set to `0`, Git will complete any requested operation without
 +      performing any optional sub-operations that require taking a lock.
 +      For example, this will prevent `git status` from refreshing the
 +      index as a side effect. This is useful for processes running in
 +      the background which do not want to cause lock contention with
 +      other operations on the repository.  Defaults to `1`.
 +
 +`GIT_REDIRECT_STDIN`::
 +`GIT_REDIRECT_STDOUT`::
 +`GIT_REDIRECT_STDERR`::
 +      Windows-only: allow redirecting the standard input/output/error
 +      handles to paths specified by the environment variables. This is
 +      particularly useful in multi-threaded applications where the
 +      canonical way to pass standard handles via `CreateProcess()` is
 +      not an option because it would require the handles to be marked
 +      inheritable (and consequently *every* spawned process would
 +      inherit them, possibly blocking regular Git operations). The
 +      primary intended use case is to use named pipes for communication
 +      (e.g. `\\.\pipe\my-git-stdin-123`).
 ++
 +Two special values are supported: `off` will simply close the
 +corresponding standard handle, and if `GIT_REDIRECT_STDERR` is
 +`2>&1`, standard error will be redirected to the same handle as
 +standard output.
 +
  Discussion[[Discussion]]
  ------------------------
  
diff --combined Makefile
index e53750ca01afbd7d777e62aa221f6861f66b0703,9ce68cded686251a11cdda3ac9e6d87283973c5b..fef9c8d2725d6806d9ef4516de64c6d68bd7a7fe
+++ b/Makefile
@@@ -205,9 -205,6 +205,9 @@@ all:
  #
  # Define NO_MMAP if you want to avoid mmap.
  #
 +# Define MMAP_PREVENTS_DELETE if a file that is currently mmapped cannot be
 +# deleted or cannot be replaced using rename().
 +#
  # Define NO_SYS_POLL_H if you don't have sys/poll.h.
  #
  # Define NO_POLL if you do not have or don't want to use poll().
@@@ -646,9 -643,7 +646,9 @@@ TEST_PROGRAMS_NEED_X += test-ctyp
  TEST_PROGRAMS_NEED_X += test-config
  TEST_PROGRAMS_NEED_X += test-date
  TEST_PROGRAMS_NEED_X += test-delta
 +TEST_PROGRAMS_NEED_X += test-drop-caches
  TEST_PROGRAMS_NEED_X += test-dump-cache-tree
 +TEST_PROGRAMS_NEED_X += test-dump-fsmonitor
  TEST_PROGRAMS_NEED_X += test-dump-split-index
  TEST_PROGRAMS_NEED_X += test-dump-untracked-cache
  TEST_PROGRAMS_NEED_X += test-fake-ssh
@@@ -796,7 -791,6 +796,7 @@@ LIB_OBJS += ewah/ewah_rlw.
  LIB_OBJS += exec_cmd.o
  LIB_OBJS += fetch-pack.o
  LIB_OBJS += fsck.o
 +LIB_OBJS += fsmonitor.o
  LIB_OBJS += gettext.o
  LIB_OBJS += gpg-interface.o
  LIB_OBJS += graph.o
@@@ -827,7 -821,6 +827,7 @@@ LIB_OBJS += notes-cache.
  LIB_OBJS += notes-merge.o
  LIB_OBJS += notes-utils.o
  LIB_OBJS += object.o
 +LIB_OBJS += oidmap.o
  LIB_OBJS += oidset.o
  LIB_OBJS += packfile.o
  LIB_OBJS += pack-bitmap.o
@@@ -849,6 -842,7 +849,7 @@@ LIB_OBJS += pretty.
  LIB_OBJS += prio-queue.o
  LIB_OBJS += progress.o
  LIB_OBJS += prompt.o
+ LIB_OBJS += protocol.o
  LIB_OBJS += quote.o
  LIB_OBJS += reachable.o
  LIB_OBJS += read-cache.o
@@@ -1398,9 -1392,6 +1399,9 @@@ els
                COMPAT_OBJS += compat/win32mmap.o
        endif
  endif
 +ifdef MMAP_PREVENTS_DELETE
 +      BASIC_CFLAGS += -DMMAP_PREVENTS_DELETE
 +endif
  ifdef OBJECT_CREATION_USES_RENAMES
        COMPAT_CFLAGS += -DOBJECT_CREATION_MODE=1
  endif
@@@ -1943,8 -1934,7 +1944,8 @@@ $(SCRIPT_LIB) : % : %.sh GIT-SCRIPT-DEF
  
  git.res: git.rc GIT-VERSION-FILE
        $(QUIET_RC)$(RC) \
 -        $(join -DMAJOR= -DMINOR=, $(wordlist 1,2,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
 +        $(join -DMAJOR= -DMINOR= -DMICRO= -DPATCHLEVEL=, $(wordlist 1, 4, \
 +          $(shell echo $(GIT_VERSION) 0 0 0 0 | tr '.a-zA-Z-' ' '))) \
          -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" -i $< -o $@
  
  # This makes sure we depend on the NO_PERL setting itself.
diff --combined builtin/receive-pack.c
index 4d37a160d759889e28602adc6064cde044248e4e,839c1462dcb4164a668635ac0a1056634642cf06..b7ce7c7f5275febbf6db6b71e3b610b09a4fe191
@@@ -24,6 -24,7 +24,7 @@@
  #include "tmp-objdir.h"
  #include "oidset.h"
  #include "packfile.h"
+ #include "protocol.h"
  
  static const char * const receive_pack_usage[] = {
        N_("git receive-pack <git-dir>"),
@@@ -870,7 -871,7 +871,7 @@@ static void refuse_unconfigured_deny_de
        rp_error("%s", _(refuse_unconfigured_deny_delete_current_msg));
  }
  
 -static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]);
 +static int command_singleton_iterator(void *cb_data, struct object_id *oid);
  static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
  {
        static struct lock_file shallow_lock;
@@@ -1139,7 -1140,7 +1140,7 @@@ static const char *update(struct comman
                }
                if (ref_transaction_delete(transaction,
                                           namespaced_name,
 -                                         old_oid ? old_oid->hash : NULL,
 +                                         old_oid,
                                           0, "push", &err)) {
                        rp_error("%s", err.buf);
                        strbuf_release(&err);
  
                if (ref_transaction_update(transaction,
                                           namespaced_name,
 -                                         new_oid->hash, old_oid->hash,
 +                                         new_oid, old_oid,
                                           0, "push",
                                           &err)) {
                        rp_error("%s", err.buf);
@@@ -1207,10 -1208,11 +1208,10 @@@ static void check_aliased_update(struc
        const char *dst_name;
        struct string_list_item *item;
        struct command *dst_cmd;
 -      unsigned char sha1[GIT_MAX_RAWSZ];
        int flag;
  
        strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
 -      dst_name = resolve_ref_unsafe(buf.buf, 0, sha1, &flag);
 +      dst_name = resolve_ref_unsafe(buf.buf, 0, NULL, &flag);
        strbuf_release(&buf);
  
        if (!(flag & REF_ISSYMREF))
@@@ -1270,7 -1272,7 +1271,7 @@@ static void check_aliased_updates(struc
        string_list_clear(&ref_list, 0);
  }
  
 -static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
 +static int command_singleton_iterator(void *cb_data, struct object_id *oid)
  {
        struct command **cmd_list = cb_data;
        struct command *cmd = *cmd_list;
        if (!cmd || is_null_oid(&cmd->new_oid))
                return -1; /* end of list */
        *cmd_list = NULL; /* this returns only one */
 -      hashcpy(sha1, cmd->new_oid.hash);
 +      oidcpy(oid, &cmd->new_oid);
        return 0;
  }
  
@@@ -1309,7 -1311,7 +1310,7 @@@ struct iterate_data 
        struct shallow_info *si;
  };
  
 -static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
 +static int iterate_receive_command_list(void *cb_data, struct object_id *oid)
  {
        struct iterate_data *data = cb_data;
        struct command **cmd_list = &data->cmds;
                        /* to be checked in update_shallow_ref() */
                        continue;
                if (!is_null_oid(&cmd->new_oid) && !cmd->skip_update) {
 -                      hashcpy(sha1, cmd->new_oid.hash);
 +                      oidcpy(oid, &cmd->new_oid);
                        *cmd_list = cmd->next;
                        return 0;
                }
@@@ -1458,6 -1460,7 +1459,6 @@@ static void execute_commands(struct com
  {
        struct check_connected_options opt = CHECK_CONNECTED_INIT;
        struct command *cmd;
 -      struct object_id oid;
        struct iterate_data data;
        struct async muxer;
        int err_fd = 0;
        check_aliased_updates(commands);
  
        free(head_name_to_free);
 -      head_name = head_name_to_free = resolve_refdup("HEAD", 0, oid.hash, NULL);
 +      head_name = head_name_to_free = resolve_refdup("HEAD", 0, NULL, NULL);
  
        if (use_atomic)
                execute_commands_atomic(commands, si);
@@@ -1961,6 -1964,22 +1962,22 @@@ int cmd_receive_pack(int argc, const ch
        else if (0 <= receive_unpack_limit)
                unpack_limit = receive_unpack_limit;
  
+       switch (determine_protocol_version_server()) {
+       case protocol_v1:
+               /*
+                * v1 is just the original protocol with a version string,
+                * so just fall through after writing the version string.
+                */
+               if (advertise_refs || !stateless_rpc)
+                       packet_write_fmt(1, "version 1\n");
+               /* fallthrough */
+       case protocol_v0:
+               break;
+       case protocol_unknown_version:
+               BUG("unknown protocol version");
+       }
        if (advertise_refs || !stateless_rpc) {
                write_head_info();
        }
diff --combined cache.h
index 2e143450514c88ec30b447c8a94e280be9105aa0,3a6b869c2a26aea682a160ac993e06ab60d2b45b..cb5db7bf83053f675acebe4f8255f52881425773
+++ b/cache.h
@@@ -204,7 -204,6 +204,7 @@@ struct cache_entry 
  #define CE_ADDED             (1 << 19)
  
  #define CE_HASHED            (1 << 20)
 +#define CE_FSMONITOR_VALID   (1 << 21)
  #define CE_WT_REMOVE         (1 << 22) /* remove in work directory */
  #define CE_CONFLICTED        (1 << 23)
  
@@@ -328,7 -327,6 +328,7 @@@ static inline unsigned int canon_mode(u
  #define CACHE_TREE_CHANGED    (1 << 5)
  #define SPLIT_INDEX_ORDERED   (1 << 6)
  #define UNTRACKED_CHANGED     (1 << 7)
 +#define FSMONITOR_CHANGED     (1 << 8)
  
  struct split_index;
  struct untracked_cache;
@@@ -347,8 -345,6 +347,8 @@@ struct index_state 
        struct hashmap dir_hash;
        unsigned char sha1[20];
        struct untracked_cache *untracked;
 +      uint64_t fsmonitor_last_update;
 +      struct ewah_bitmap *fsmonitor_dirty;
  };
  
  extern struct index_state the_index;
@@@ -448,8 -444,17 +448,18 @@@ static inline enum object_type object_t
  #define GIT_NOGLOB_PATHSPECS_ENVIRONMENT "GIT_NOGLOB_PATHSPECS"
  #define GIT_ICASE_PATHSPECS_ENVIRONMENT "GIT_ICASE_PATHSPECS"
  #define GIT_QUARANTINE_ENVIRONMENT "GIT_QUARANTINE_PATH"
 +#define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
  
+ /*
+  * Environment variable used in handshaking the wire protocol.
+  * Contains a colon ':' separated list of keys with optional values
+  * 'key[=value]'.  Presence of unknown keys and values must be
+  * ignored.
+  */
+ #define GIT_PROTOCOL_ENVIRONMENT "GIT_PROTOCOL"
+ /* HTTP header used to handshake the wire protocol */
+ #define GIT_PROTOCOL_HEADER "Git-Protocol"
  /*
   * This environment variable is expected to contain a boolean indicating
   * whether we should or should not treat:
@@@ -606,28 -611,9 +616,28 @@@ extern int do_read_index(struct index_s
  extern int read_index_from(struct index_state *, const char *path);
  extern int is_index_unborn(struct index_state *);
  extern int read_index_unmerged(struct index_state *);
 +
 +/* For use with `write_locked_index()`. */
  #define COMMIT_LOCK           (1 << 0)
 -#define CLOSE_LOCK            (1 << 1)
 +
 +/*
 + * Write the index while holding an already-taken lock. Close the lock,
 + * and if `COMMIT_LOCK` is given, commit it.
 + *
 + * Unless a split index is in use, write the index into the lockfile.
 + *
 + * With a split index, write the shared index to a temporary file,
 + * adjust its permissions and rename it into place, then write the
 + * split index to the lockfile. If the temporary file for the shared
 + * index cannot be created, fall back to the behavior described in
 + * the previous paragraph.
 + *
 + * With `COMMIT_LOCK`, the lock is always committed or rolled back.
 + * Without it, the lock is closed, but neither committed nor rolled
 + * back.
 + */
  extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
 +
  extern int discard_index(struct index_state *);
  extern void move_index_extensions(struct index_state *dst, struct index_state *src);
  extern int unmerged_index(const struct index_state *);
@@@ -704,14 -690,11 +714,14 @@@ extern void *read_blob_data_from_index(
  #define CE_MATCH_IGNORE_MISSING               0x08
  /* enable stat refresh */
  #define CE_MATCH_REFRESH              0x10
 -extern int ie_match_stat(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 -extern int ie_modified(const struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 +/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
 +#define CE_MATCH_IGNORE_FSMONITOR 0X20
 +extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
 +extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
  
  #define HASH_WRITE_OBJECT 1
  #define HASH_FORMAT_CHECK 2
 +#define HASH_RENORMALIZE  4
  extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
  extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
  
@@@ -742,17 -725,12 +752,17 @@@ extern void fill_stat_cache_info(struc
  extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
  extern struct cache_entry *refresh_cache_entry(struct cache_entry *, unsigned int);
  
 +/*
 + * Opportunistically update the index but do not complain if we can't.
 + * The lockfile is always committed or rolled back.
 + */
  extern void update_index_if_able(struct index_state *, struct lock_file *);
  
  extern int hold_locked_index(struct lock_file *, int);
  extern void set_alternate_index_output(const char *);
  
  extern int verify_index_checksum;
 +extern int verify_ce_order;
  
  /* Environment bits from configuration mechanism */
  extern int trust_executable_bit;
@@@ -806,7 -784,6 +816,7 @@@ extern int core_apply_sparse_checkout
  extern int precomposed_unicode;
  extern int protect_hfs;
  extern int protect_ntfs;
 +extern const char *core_fsmonitor;
  
  /*
   * Include broken refs in all ref iterations, which will
   */
  extern int ref_paranoia;
  
 +/*
 + * Returns the boolean value of $GIT_OPTIONAL_LOCKS (or the default value).
 + */
 +int use_optional_locks(void);
 +
  /*
   * The character that begins a commented line in user-editable file
   * that is subject to stripspace.
@@@ -1282,8 -1254,8 +1292,8 @@@ static inline unsigned int hexval(unsig
   */
  static inline int hex2chr(const char *s)
  {
 -      int val = hexval(s[0]);
 -      return (val < 0) ? val : (val << 4) | hexval(s[1]);
 +      unsigned int val = hexval(s[0]);
 +      return (val & ~0xf) ? val : (val << 4) | hexval(s[1]);
  }
  
  /* Convert to/from hex/sha1 representation */
@@@ -1349,13 -1321,6 +1359,13 @@@ extern int set_disambiguate_hint_config
  extern int get_sha1_hex(const char *hex, unsigned char *sha1);
  extern int get_oid_hex(const char *hex, struct object_id *sha1);
  
 +/*
 + * Read `len` pairs of hexadecimal digits from `hex` and write the
 + * values to `binary` as `len` bytes. Return 0 on success, or -1 if
 + * the input does not consist of hex digits).
 + */
 +extern int hex_to_bytes(unsigned char *binary, const char *hex, size_t len);
 +
  /*
   * Convert a binary sha1 to its hex equivalent. The `_r` variant is reentrant,
   * and writes the NUL-terminated output to the buffer `out`, which must be at
diff --combined http.c
index 713525f38ed3f254aed2b0dd65fece7972444072,ffb719216179dba7490c5cdffe8e0f72d3955aeb..215bebef1bfb935355a765027fc8605fccbc3d1b
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -12,6 -12,7 +12,7 @@@
  #include "gettext.h"
  #include "transport.h"
  #include "packfile.h"
+ #include "protocol.h"
  
  static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
  #if LIBCURL_VERSION_NUM >= 0x070a08
@@@ -638,7 -639,9 +639,7 @@@ static int curl_trace(CURL *handle, cur
        switch (type) {
        case CURLINFO_TEXT:
                trace_printf_key(&trace_curl, "== Info: %s", data);
 -      default:                /* we ignore unknown types by default */
 -              return 0;
 -
 +              break;
        case CURLINFO_HEADER_OUT:
                text = "=> Send header";
                curl_dump_header(text, (unsigned char *)data, size, DO_FILTER);
                text = "<= Recv SSL data";
                curl_dump_data(text, (unsigned char *)data, size);
                break;
 +
 +      default:                /* we ignore unknown types by default */
 +              return 0;
        }
        return 0;
  }
@@@ -898,6 -898,21 +899,21 @@@ static void set_from_env(const char **v
                *var = val;
  }
  
+ static void protocol_http_header(void)
+ {
+       if (get_protocol_version_config() > 0) {
+               struct strbuf protocol_header = STRBUF_INIT;
+               strbuf_addf(&protocol_header, GIT_PROTOCOL_HEADER ": version=%d",
+                           get_protocol_version_config());
+               extra_http_headers = curl_slist_append(extra_http_headers,
+                                                      protocol_header.buf);
+               strbuf_release(&protocol_header);
+       }
+ }
  void http_init(struct remote *remote, const char *url, int proactive_auth)
  {
        char *low_speed_limit;
        if (remote)
                var_override(&http_proxy_authmethod, remote->http_proxy_authmethod);
  
+       protocol_http_header();
        pragma_header = curl_slist_append(http_copy_default_headers(),
                "Pragma: no-cache");
        no_pragma_header = curl_slist_append(http_copy_default_headers(),
diff --combined pkt-line.c
index 93ea311443a37b81b85a72dc569c715417d7ef29,7006b35879529f5f1a26cb5b6a628fe28aebe6a7..2827ca772a3703f71bc588d0f6cacd5caa318fe7
@@@ -188,6 -188,12 +188,12 @@@ static int packet_write_gently(const in
        return 0;
  }
  
+ void packet_write(int fd_out, const char *buf, size_t size)
+ {
+       if (packet_write_gently(fd_out, buf, size))
+               die_errno("packet write failed");
+ }
  void packet_buf_write(struct strbuf *buf, const char *fmt, ...)
  {
        va_list args;
@@@ -258,7 -264,7 +264,7 @@@ static int get_packet_data(int fd, cha
        }
  
        /* And complain if we didn't get enough bytes to satisfy the read. */
 -      if (ret < size) {
 +      if (ret != size) {
                if (options & PACKET_READ_GENTLE_ON_EOF)
                        return -1;
  
diff --combined t/t5601-clone.sh
index 50e40abb11e1d317a2a58321f1007dde925c8da2,86811a0c35f0a1baf23ce81a5829adf7b896548a..ef94af9fccf1f76f4b0b86994ea823a37e996b33
@@@ -308,10 -308,9 +308,10 @@@ test_expect_success 'clone checking ou
  
  setup_ssh_wrapper () {
        test_expect_success 'setup ssh wrapper' '
-               rm -f "$TRASH_DIRECTORY/ssh-wrapper$X" &&
++              rm -f "$TRASH_DIRECTORY/ssh$X" &&
                cp "$GIT_BUILD_DIR/t/helper/test-fake-ssh$X" \
-                       "$TRASH_DIRECTORY/ssh-wrapper$X" &&
-               GIT_SSH="$TRASH_DIRECTORY/ssh-wrapper$X" &&
+                       "$TRASH_DIRECTORY/ssh$X" &&
+               GIT_SSH="$TRASH_DIRECTORY/ssh$X" &&
                export GIT_SSH &&
                export TRASH_DIRECTORY &&
                >"$TRASH_DIRECTORY"/ssh-output
  }
  
  copy_ssh_wrapper_as () {
-       cp "$TRASH_DIRECTORY/ssh-wrapper$X" "${1%$X}$X" &&
 +      rm -f "${1%$X}$X" &&
+       cp "$TRASH_DIRECTORY/ssh$X" "${1%$X}$X" &&
        GIT_SSH="${1%$X}$X" &&
        export GIT_SSH
  }
@@@ -364,10 -362,26 +364,26 @@@ test_expect_success 'bracketed hostname
        expect_ssh "-p 123" myhost src
  '
  
- test_expect_success 'uplink is not treated as putty' '
+ test_expect_success 'OpenSSH variant passes -4' '
+       git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
+       expect_ssh "-4 -p 123" myhost src
+ '
+ test_expect_success 'variant can be overriden' '
+       git -c ssh.variant=simple clone -4 "[myhost:123]:src" ssh-simple-clone &&
+       expect_ssh myhost src
+ '
+ test_expect_success 'simple is treated as simple' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/simple" &&
+       git clone -4 "[myhost:123]:src" ssh-bracket-clone-simple &&
+       expect_ssh myhost src
+ '
+ test_expect_success 'uplink is treated as simple' '
        copy_ssh_wrapper_as "$TRASH_DIRECTORY/uplink" &&
        git clone "[myhost:123]:src" ssh-bracket-clone-uplink &&
-       expect_ssh "-p 123" myhost src
+       expect_ssh myhost src
  '
  
  test_expect_success 'plink is treated specially (as putty)' '
diff --combined upload-pack.c
index 6d5f3c0d39a886f5010115927d71f205a96d0e04,ef99a029c12efcde3f686b51f3693ca4c8d05b1e..d5de18127c63a9d017578e0969743392d39526d4
@@@ -18,6 -18,7 +18,7 @@@
  #include "parse-options.h"
  #include "argv-array.h"
  #include "prio-queue.h"
+ #include "protocol.h"
  
  static const char * const upload_pack_usage[] = {
        N_("git upload-pack [<options>] <dir>"),
@@@ -787,7 -788,7 +788,7 @@@ static void receive_needs(void
                if (skip_prefix(line, "deepen-not ", &arg)) {
                        char *ref = NULL;
                        struct object_id oid;
 -                      if (expand_ref(arg, strlen(arg), oid.hash, &ref) != 1)
 +                      if (expand_ref(arg, strlen(arg), &oid, &ref) != 1)
                                die("git upload-pack: ambiguous deepen-not: %s", line);
                        string_list_append(&deepen_not, ref);
                        free(ref);
                }
  
        shallow_nr += shallows.nr;
 -      free(shallows.objects);
 +      object_array_clear(&shallows);
  }
  
  /* return non-zero if the ref is hidden, otherwise 0 */
@@@ -955,7 -956,7 +956,7 @@@ static int send_ref(const char *refname
                packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), refname_nons);
        }
        capabilities = NULL;
 -      if (!peel_ref(refname, peeled.hash))
 +      if (!peel_ref(refname, &peeled))
                packet_write_fmt(1, "%s %s^{}\n", oid_to_hex(&peeled), refname_nons);
        return 0;
  }
@@@ -965,10 -966,11 +966,10 @@@ static int find_symref(const char *refn
  {
        const char *symref_target;
        struct string_list_item *item;
 -      struct object_id unused;
  
        if ((flag & REF_ISSYMREF) == 0)
                return 0;
 -      symref_target = resolve_ref_unsafe(refname, 0, unused.hash, &flag);
 +      symref_target = resolve_ref_unsafe(refname, 0, NULL, &flag);
        if (!symref_target || (flag & REF_ISSYMREF) == 0)
                die("'%s' is a symref but it is not?", refname);
        item = string_list_append(cb_data, refname);
@@@ -1066,6 -1068,23 +1067,23 @@@ int cmd_main(int argc, const char **arg
                die("'%s' does not appear to be a git repository", dir);
  
        git_config(upload_pack_config, NULL);
-       upload_pack();
+       switch (determine_protocol_version_server()) {
+       case protocol_v1:
+               /*
+                * v1 is just the original protocol with a version string,
+                * so just fall through after writing the version string.
+                */
+               if (advertise_refs || !stateless_rpc)
+                       packet_write_fmt(1, "version 1\n");
+               /* fallthrough */
+       case protocol_v0:
+               upload_pack();
+               break;
+       case protocol_unknown_version:
+               BUG("unknown protocol version");
+       }
        return 0;
  }