Merge branch 'jc/url-match'
authorJunio C Hamano <gitster@pobox.com>
Mon, 9 Sep 2013 21:50:32 +0000 (14:50 -0700)
committerJunio C Hamano <gitster@pobox.com>
Mon, 9 Sep 2013 21:50:36 +0000 (14:50 -0700)
Allow section.<urlpattern>.var configuration variables to be
treated as a "virtual" section.var given a URL, and use the
mechanism to enhance http.* configuration variables.

This is a reroll of Kyle J. McKay's work.

* jc/url-match:
builtin/config.c: compilation fix
config: "git config --get-urlmatch" parses section.<url>.key
builtin/config: refactor collect_config()
config: parse http.<url>.<variable> using urlmatch
config: add generic callback wrapper to parse section.<url>.key
config: add helper to normalize and match URLs
http.c: fix parsing of http.sslCertPasswordProtected variable

1  2 
.gitignore
Documentation/config.txt
Documentation/git-config.txt
Makefile
builtin/config.c
http.c
diff --combined .gitignore
index 6b1fd1bfb060c438d7b70684612cef08fc1a5859,b8524bfe489352210ddabae36ac93a4f518a3d7b..66199edd4af6ddcd1398bff13fe9065ba4309a22
@@@ -23,7 -23,6 +23,7 @@@
  /git-cat-file
  /git-check-attr
  /git-check-ignore
 +/git-check-mailmap
  /git-check-ref-format
  /git-checkout
  /git-checkout-index
  /git-remote-ftps
  /git-remote-fd
  /git-remote-ext
 +/git-remote-testgit
  /git-remote-testpy
  /git-remote-testsvn
  /git-repack
  /test-mktemp
  /test-parse-options
  /test-path-utils
 +/test-prio-queue
 +/test-read-cache
  /test-regex
  /test-revision-walking
  /test-run-command
  /test-string-list
  /test-subprocess
  /test-svn-fe
+ /test-urlmatch-normalization
  /test-wildmatch
  /common-cmds.h
  *.tar.gz
  /cscope*
  *.obj
  *.lib
 +*.res
  *.sln
  *.suo
  *.ncb
diff --combined Documentation/config.txt
index e8eadde30577dd7a08ef412d24f857638ff47880,a81f3ab74e380e70eb001eb8ca878ed54199475a..00bde9dec54160ffc4d06e0169f2058176291928
@@@ -199,9 -199,6 +199,9 @@@ advice.*:
        amWorkDir::
                Advice that shows the location of the patch file when
                linkgit:git-am[1] fails to apply it.
 +      rmHints::
 +              In case of failure in the output of linkgit:git-rm[1],
 +              show directions on how to proceed from the current state.
  --
  
  core.fileMode::
@@@ -213,6 -210,17 +213,6 @@@ The default is true, except linkgit:git
  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,
 -      the Cygwin stat() and lstat() functions are used. This may be useful
 -      if your repository consists of a few separate directories joined in
 -      one hierarchy using Cygwin mount. If true, Git uses native Win32 API
 -      whenever it is possible and falls back to Cygwin functions only to
 -      handle symbol links. The native mode is more than twice faster than
 -      normal Cygwin l/stat() functions. True by default, unless core.filemode
 -      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,
@@@ -868,17 -876,16 +868,17 @@@ The values of these variables may be sp
  
  color.interactive::
        When set to `always`, always use colors for interactive prompts
 -      and displays (such as those used by "git-add --interactive").
 -      When false (or `never`), never.  When set to `true` or `auto`, use
 -      colors only when the output is to the terminal. Defaults to false.
 +      and displays (such as those used by "git-add --interactive" and
 +      "git-clean --interactive"). When false (or `never`), never.
 +      When set to `true` or `auto`, use colors only when the output is
 +      to the terminal. Defaults to false.
  
  color.interactive.<slot>::
 -      Use customized color for 'git add --interactive'
 -      output. `<slot>` may be `prompt`, `header`, `help` or `error`, for
 -      four distinct types of normal output from interactive
 -      commands.  The values of these variables may be specified as
 -      in color.branch.<slot>.
 +      Use customized color for 'git add --interactive' and 'git clean
 +      --interactive' output. `<slot>` may be `prompt`, `header`, `help`
 +      or `error`, for four distinct types of normal output from
 +      interactive commands.  The values of these variables may be
 +      specified as in color.branch.<slot>.
  
  color.pager::
        A boolean to enable/disable colored output when the pager is in
@@@ -912,21 -919,17 +912,21 @@@ color.ui:
        as `color.diff` and `color.grep` that control the use of color
        per command family. Its scope will expand as more commands learn
        configuration to set a default for the `--color` option.  Set it
 -      to `always` if you want all output not intended for machine
 -      consumption to use color, to `true` or `auto` if you want such
 -      output to use color when written to the terminal, or to `false` or
 -      `never` if you prefer Git commands not to use color unless enabled
 -      explicitly with some other configuration or the `--color` option.
 +      to `false` or `never` if you prefer Git commands not to use
 +      color unless enabled explicitly with some other configuration
 +      or the `--color` option. Set it to `always` if you want all
 +      output not intended for machine consumption to use color, to
 +      `true` or `auto` (this is the default since Git 1.8.4) if you
 +      want such output to use color when written to the terminal.
  
  column.ui::
        Specify whether supported commands should output in columns.
        This variable consists of a list of tokens separated by spaces
        or commas:
  +
 +These options control when the feature should be enabled
 +(defaults to 'never'):
 ++
  --
  `always`;;
        always show in columns
        never show in columns
  `auto`;;
        show in columns if the output is to the terminal
 +--
 ++
 +These options control layout (defaults to 'column').  Setting any
 +of these implies 'always' if none of 'always', 'never', or 'auto' are
 +specified.
 ++
 +--
  `column`;;
 -      fill columns before rows (default)
 +      fill columns before rows
  `row`;;
        fill rows before columns
  `plain`;;
        show in one column
 +--
 ++
 +Finally, these options can be combined with a layout option (defaults
 +to 'nodense'):
 ++
 +--
  `dense`;;
        make unequal size columns to utilize more space
  `nodense`;;
        make equal size columns
  --
 -+
 -This option defaults to 'never'.
  
  column.branch::
        Specify whether to output branch listing in `git branch` in columns.
        See `column.ui` for details.
  
 +column.clean::
 +      Specify the layout when list items in `git clean -i`, which always
 +      shows files and directories in columns. See `column.ui` for details.
 +
  column.status::
        Specify whether to output untracked files in `git status` in columns.
        See `column.ui` for details.
@@@ -1061,10 -1049,6 +1061,10 @@@ fetch.unpackLimit:
        especially on slow filesystems.  If not set, the value of
        `transfer.unpackLimit` is used instead.
  
 +fetch.prune::
 +      If true, fetch will automatically behave as if the `--prune`
 +      option was given on the command line.  See also `remote.<name>.prune`.
 +
  format.attach::
        Enable multipart/mixed attachments as the default for
        'format-patch'.  The value can also be a double quoted string
@@@ -1449,11 -1433,7 +1449,11 @@@ http.cookiefile:
        of the file to read cookies from should be plain HTTP headers or
        the Netscape/Mozilla cookie file format (see linkgit:curl[1]).
        NOTE that the file specified with http.cookiefile is only used as
 -      input. No cookies will be stored in the file.
 +      input unless http.saveCookies is set.
 +
 +http.savecookies::
 +      If set, store cookies received during requests to the file specified by
 +      http.cookiefile. Has no effect if http.cookiefile is unset.
  
  http.sslVerify::
        Whether to verify the SSL certificate when fetching or pushing
@@@ -1533,6 -1513,51 +1533,51 @@@ http.useragent:
        of common USER_AGENT strings (but not including those like git/1.7.1).
        Can be overridden by the 'GIT_HTTP_USER_AGENT' environment variable.
  
+ http.<url>.*::
+       Any of the http.* options above can be applied selectively to some urls.
+       For a config key to match a URL, each element of the config key is
+       compared to that of the URL, in the following order:
+ +
+ --
+ . Scheme (e.g., `https` in `https://example.com/`). This field
+   must match exactly between the config key and the URL.
+ . Host/domain name (e.g., `example.com` in `https://example.com/`).
+   This field must match exactly between the config key and the URL.
+ . Port number (e.g., `8080` in `http://example.com:8080/`).
+   This field must match exactly between the config key and the URL.
+   Omitted port numbers are automatically converted to the correct
+   default for the scheme before matching.
+ . Path (e.g., `repo.git` in `https://example.com/repo.git`). The
+   path field of the config key must match the path field of the URL
+   either exactly or as a prefix of slash-delimited path elements.  This means
+   a config key with path `foo/` matches URL path `foo/bar`.  A prefix can only
+   match on a slash (`/`) boundary.  Longer matches take precedence (so a config
+   key with path `foo/bar` is a better match to URL path `foo/bar` than a config
+   key with just path `foo/`).
+ . User name (e.g., `user` in `https://user@example.com/repo.git`). If
+   the config key has a user name it must match the user name in the
+   URL exactly. If the config key does not have a user name, that
+   config key will match a URL with any user name (including none),
+   but at a lower precedence than a config key with a user name.
+ --
+ +
+ The list above is ordered by decreasing precedence; a URL that matches
+ a config key's path is preferred to one that matches its user name. For example,
+ if the URL is `https://user@example.com/foo/bar` a config key match of
+ `https://example.com/foo` will be preferred over a config key match of
+ `https://user@example.com`.
+ +
+ All URLs are normalized before attempting any matching (the password part,
+ if embedded in the URL, is always ignored for matching purposes) so that
+ equivalent urls that are simply spelled differently will match properly.
+ Environment variable settings always override any matches.  The urls that are
+ matched against are those given directly to Git commands.  This means any URLs
+ visited as a result of a redirection do not participate in matching.
  i18n.commitEncoding::
        Character encoding the commit messages are stored in; Git itself
        does not care per se, but this information is necessary e.g. when
@@@ -1846,59 -1871,39 +1891,59 @@@ pull.twohead:
        The default merge strategy to use when pulling a single branch.
  
  push.default::
 -      Defines the action `git push` should take if no refspec is given
 -      on the command line, no refspec is configured in the remote, and
 -      no refspec is implied by any of the options given on the command
 -      line. Possible values are:
 +      Defines the action `git push` should take if no refspec is
 +      explicitly given.  Different values are well-suited for
 +      specific workflows; for instance, in a purely central workflow
 +      (i.e. the fetch source is equal to the push destination),
 +      `upstream` is probably what you want.  Possible values are:
  +
  --
 -* `nothing` - do not push anything.
 -* `matching` - push all branches having the same name in both ends.
 -  This is for those who prepare all the branches into a publishable
 -  shape and then push them out with a single command.  It is not
 -  appropriate for pushing into a repository shared by multiple users,
 -  since locally stalled branches will attempt a non-fast forward push
 -  if other users updated the branch.
 -  +
 -  This is currently the default, but Git 2.0 will change the default
 -  to `simple`.
 -* `upstream` - push the current branch to its upstream branch
 -  (`tracking` is a deprecated synonym for this).
 -  With this, `git push` will update the same remote ref as the one which
 -  is merged by `git pull`, making `push` and `pull` symmetrical.
 -  See "branch.<name>.merge" for how to configure the upstream branch.
 -* `simple` - like `upstream`, but refuses to push if the upstream
 -  branch's name is different from the local one. This is the safest
 -  option and is well-suited for beginners. It will become the default
 -  in Git 2.0.
 -* `current` - push the current branch to a branch of the same name.
 ---
 +
 +* `nothing` - do not push anything (error out) unless a refspec is
 +  explicitly given. This is primarily meant for people who want to
 +  avoid mistakes by always being explicit.
 +
 +* `current` - push the current branch to update a branch with the same
 +  name on the receiving end.  Works in both central and non-central
 +  workflows.
 +
 +* `upstream` - push the current branch back to the branch whose
 +  changes are usually integrated into the current branch (which is
 +  called `@{upstream}`).  This mode only makes sense if you are
 +  pushing to the same repository you would normally pull from
 +  (i.e. central workflow).
 +
 +* `simple` - in centralized workflow, work like `upstream` with an
 +  added safety to refuse to push if the upstream branch's name is
 +  different from the local one.
  +
 -The `simple`, `current` and `upstream` modes are for those who want to
 -push out a single branch after finishing work, even when the other
 -branches are not yet ready to be pushed out. If you are working with
 -other people to push into the same shared repository, you would want
 -to use one of these.
 +When pushing to a remote that is different from the remote you normally
 +pull from, work as `current`.  This is the safest option and is suited
 +for beginners.
 ++
 +This mode will become the default in Git 2.0.
 +
 +* `matching` - push all branches having the same name on both ends.
 +  This makes the repository you are pushing to remember the set of
 +  branches that will be pushed out (e.g. if you always push 'maint'
 +  and 'master' there and no other branches, the repository you push
 +  to will have these two branches, and your local 'maint' and
 +  'master' will be pushed there).
 ++
 +To use this mode effectively, you have to make sure _all_ the
 +branches you would push out are ready to be pushed out before
 +running 'git push', as the whole point of this mode is to allow you
 +to push all of the branches in one go.  If you usually finish work
 +on only one branch and push out the result, while other branches are
 +unfinished, this mode is not for you.  Also this mode is not
 +suitable for pushing into a shared central repository, as other
 +people may add new branches there, or update the tip of existing
 +branches outside your control.
 ++
 +This is currently the default, but Git 2.0 will change the default
 +to `simple`.
 +
 +--
  
  rebase.stat::
        Whether to show a diffstat of what changed upstream since the last
  rebase.autosquash::
        If set to true enable '--autosquash' option by default.
  
 +rebase.autostash::
 +      When set to true, automatically create a temporary stash
 +      before the operation begins, and apply it after the operation
 +      ends.  This means that you can run rebase on a dirty worktree.
 +      However, use with care: the final stash application after a
 +      successful rebase might result in non-trivial conflicts.
 +      Defaults to false.
 +
  receive.autogc::
        By default, git-receive-pack will run "git-gc --auto" after
        receiving data from git-push and updating refs.  You can stop
@@@ -2032,12 -2029,6 +2077,12 @@@ remote.<name>.vcs:
        Setting this to a value <vcs> will cause Git to interact with
        the remote with the git-remote-<vcs> helper.
  
 +remote.<name>.prune::
 +      When set to true, fetching from this remote by default will also
 +      remove any remote-tracking branches which no longer exist on the
 +      remote (as if the `--prune` option was give on the command line).
 +      Overrides `fetch.prune` settings, if any.
 +
  remotes.<group>::
        The list of remotes which are fetched by "git remote update
        <group>".  See linkgit:git-remote[1].
@@@ -2076,10 -2067,6 +2121,10 @@@ sendemail.smtpencryption:
  sendemail.smtpssl::
        Deprecated alias for 'sendemail.smtpencryption = ssl'.
  
 +sendemail.smtpsslcertpath::
 +      Path to ca-certificates (either a directory or a single file).
 +      Set it to an empty string to disable certificate verification.
 +
  sendemail.<identity>.*::
        Identity-specific versions of the 'sendemail.*' parameters
        found below, taking precedence over those when the this
@@@ -2124,14 -2111,6 +2169,14 @@@ status.relativePaths:
        relative to the repository root (this was the default for Git
        prior to v1.5.4).
  
 +status.short::
 +      Set to true to enable --short by default in linkgit:git-status[1].
 +      The option --no-short takes precedence over this variable.
 +
 +status.branch::
 +      Set to true to enable --branch by default in linkgit:git-status[1].
 +      The option --no-branch takes precedence over this variable.
 +
  status.showUntrackedFiles::
        By default, linkgit:git-status[1] and linkgit:git-commit[1] show
        files which are not currently tracked by Git. Directories which
index 2dbe486eb16d8edde01ece856fa1e0ad75053042,b48e2ecc1ca146c0496902327b3f2a54a58e9a5a..e9917b89a91b36c87892ea1c08522f17be3c0d89
@@@ -15,6 -15,7 +15,7 @@@ SYNOPSI
  'git config' [<file-option>] [type] [-z|--null] --get name [value_regex]
  'git config' [<file-option>] [type] [-z|--null] --get-all name [value_regex]
  'git config' [<file-option>] [type] [-z|--null] --get-regexp name_regex [value_regex]
+ 'git config' [<file-option>] [type] [-z|--null] --get-urlmatch name URL
  'git config' [<file-option>] --unset name [value_regex]
  'git config' [<file-option>] --unset-all name [value_regex]
  'git config' [<file-option>] --rename-section old_name new_name
@@@ -82,7 -83,7 +83,7 @@@ OPTION
  --get::
        Get the value for a given key (optionally filtered by a regex
        matching the value). Returns error code 1 if the key was not
 -      found and error code 2 if multiple key values were found.
 +      found and the last value if multiple key values were found.
  
  --get-all::
        Like get, but does not fail if the number of values for the key
        in which section and variable names are lowercased, but subsection
        names are not.
  
+ --get-urlmatch name URL::
+       When given a two-part name section.key, the value for
+       section.<url>.key whose <url> part matches the best to the
+       given URL is returned (if no such key exists, the value for
+       section.key is used as a fallback).  When given just the
+       section as name, do so for all the keys in the section and
+       list them.
  --global::
 -      For writing options: write to global ~/.gitconfig file rather than
 -      the repository .git/config, write to $XDG_CONFIG_HOME/git/config file
 -      if this file exists and the ~/.gitconfig file doesn't.
 +      For writing options: write to global `~/.gitconfig` file
 +      rather than the repository `.git/config`, write to
 +      `$XDG_CONFIG_HOME/git/config` file if this file exists and the
 +      `~/.gitconfig` file doesn't.
  +
 -For reading options: read only from global ~/.gitconfig and from
 -$XDG_CONFIG_HOME/git/config rather than from all available files.
 +For reading options: read only from global `~/.gitconfig` and from
 +`$XDG_CONFIG_HOME/git/config` rather than from all available files.
  +
  See also <<FILES>>.
  
  --system::
 -      For writing options: write to system-wide $(prefix)/etc/gitconfig
 -      rather than the repository .git/config.
 +      For writing options: write to system-wide
 +      `$(prefix)/etc/gitconfig` rather than the repository
 +      `.git/config`.
  +
 -For reading options: read only from system-wide $(prefix)/etc/gitconfig
 +For reading options: read only from system-wide `$(prefix)/etc/gitconfig`
  rather than from all available files.
  +
  See also <<FILES>>.
  
 +--local::
 +      For writing options: write to the repository `.git/config` file.
 +      This is the default behavior.
 ++
 +For reading options: read only from the repository `.git/config` rather than
 +from all available files.
 ++
 +See also <<FILES>>.
 +
  -f config-file::
  --file config-file::
        Use the given config file instead of the one specified by GIT_CONFIG.
  
 +--blob blob::
 +      Similar to '--file' but use the given blob instead of a file. E.g.
 +      you can use 'master:.gitmodules' to read values from the file
 +      '.gitmodules' in the master branch. See "SPECIFYING REVISIONS"
 +      section in linkgit:gitrevisions[7] for a more complete list of
 +      ways to spell blob names.
 +
  --remove-section::
        Remove the given section from the configuration file.
  
@@@ -215,23 -206,23 +224,23 @@@ FILE
  If not set explicitly with '--file', there are four files where
  'git config' will search for configuration options:
  
 -$GIT_DIR/config::
 -      Repository specific configuration file.
 -
 -~/.gitconfig::
 -      User-specific configuration file. Also called "global"
 -      configuration file.
 +$(prefix)/etc/gitconfig::
 +      System-wide configuration file.
  
  $XDG_CONFIG_HOME/git/config::
        Second user-specific configuration file. If $XDG_CONFIG_HOME is not set
 -      or empty, $HOME/.config/git/config will be used. Any single-valued
 +      or empty, `$HOME/.config/git/config` will be used. Any single-valued
        variable set in this file will be overwritten by whatever is in
 -      ~/.gitconfig.  It is a good idea not to create this file if
 +      `~/.gitconfig`.  It is a good idea not to create this file if
        you sometimes use older versions of Git, as support for this
        file was added fairly recently.
  
 -$(prefix)/etc/gitconfig::
 -      System-wide configuration file.
 +~/.gitconfig::
 +      User-specific configuration file. Also called "global"
 +      configuration file.
 +
 +$GIT_DIR/config::
 +      Repository specific configuration file.
  
  If no further options are given, all reading options will read all of these
  files that are available. If the global or the system-wide configuration
@@@ -239,10 -230,6 +248,10 @@@ file are not available they will be ign
  file is not available or readable, 'git config' will exit with a non-zero
  error code. However, in neither case will an error message be issued.
  
 +The files are read in the order given above, with last value found taking
 +precedence over values read earlier.  When multiple values are taken then all
 +values of a key from all files will be used.
 +
  All writing options will per default write to the repository specific
  configuration file. Note that this also affects options like '--replace-all'
  and '--unset'. *'git config' will only ever change one file at a time*.
@@@ -295,6 -282,13 +304,13 @@@ Given a .git/config like this
                gitproxy=proxy-command for kernel.org
                gitproxy=default-proxy ; for all the rest
  
+       ; HTTP
+       [http]
+               sslVerify
+       [http "https://weak.example.com"]
+               sslVerify = false
+               cookieFile = /tmp/cookie.txt
  you can set the filemode to true with
  
  ------------
@@@ -380,6 -374,19 +396,19 @@@ RESET=$(git config --get-color "" "rese
  echo "${WS}your whitespace color or blue reverse${RESET}"
  ------------
  
+ For URLs in `https://weak.example.com`, `http.sslVerify` is set to
+ false, while it is set to `true` for all others:
+ ------------
+ % git config --bool --get-urlmatch http.sslverify https://good.example.com
+ true
+ % git config --bool --get-urlmatch http.sslverify https://weak.example.com
+ false
+ % git config --get-urlmatch http https://weak.example.com
+ http.cookiefile /tmp/cookie.txt
+ http.sslverify false
+ ------------
  include::config.txt[]
  
  GIT
diff --combined Makefile
index 7051956333422b5d355528568706e4a675d1a246,2df742c24d5fa80bd9490dfd2754b60587883c61..9135cc9358fb3cb8305eebe8ef209652f758a395
+++ b/Makefile
@@@ -69,9 -69,6 +69,9 @@@ all:
  # Define NO_MSGFMT_EXTENDED_OPTIONS if your implementation of msgfmt
  # doesn't support GNU extensions like --check and --statistics
  #
 +# Define NEEDS_CLIPPED_WRITE if your write(2) cannot write more than
 +# INT_MAX bytes at once (e.g. MacOS X).
 +#
  # Define HAVE_PATHS_H if you have paths.h and want to use the default PATH
  # it specifies.
  #
  # Define NO_FNMATCH_CASEFOLD if your fnmatch function doesn't have the
  # FNM_CASEFOLD GNU extension.
  #
 -# Define USE_WILDMATCH if you want to use Git's wildmatch
 +# Define NO_WILDMATCH if you do not want to use Git's wildmatch
  # implementation as fnmatch
  #
  # Define NO_GECOS_IN_PWENT if you don't have pw_gecos in struct passwd
  # specify your own (or DarwinPort's) include directories and
  # library directories by defining CFLAGS and LDFLAGS appropriately.
  #
 +# Define NO_APPLE_COMMON_CRYPTO if you are building on Darwin/Mac OS X
 +# and do not want to use Apple's CommonCrypto library.  This allows you
 +# to provide your own OpenSSL library, for example from MacPorts.
 +#
  # Define BLK_SHA1 environment variable to make use of the bundled
  # optimized C SHA1 routine.
  #
  #
  # Define NO_REGEX if you have no or inferior regex support in your C library.
  #
 -# Define CYGWIN_V15_WIN32API if you are using Cygwin v1.7.x but are not
 -# using the current w32api packages. The recommended approach, however,
 -# is to update your installation if compilation errors occur.
 -#
  # Define HAVE_DEV_TTY if your system can open /dev/tty to interact with the
  # user.
  #
@@@ -463,7 -460,6 +463,7 @@@ SCRIPT_SH += git-mergetool.s
  SCRIPT_SH += git-pull.sh
  SCRIPT_SH += git-quiltimport.sh
  SCRIPT_SH += git-rebase.sh
 +SCRIPT_SH += git-remote-testgit.sh
  SCRIPT_SH += git-repack.sh
  SCRIPT_SH += git-request-pull.sh
  SCRIPT_SH += git-stash.sh
@@@ -491,18 -487,11 +491,18 @@@ SCRIPT_PERL += git-svn.per
  SCRIPT_PYTHON += git-remote-testpy.py
  SCRIPT_PYTHON += git-p4.py
  
 +NO_INSTALL += git-remote-testgit
 +NO_INSTALL += git-remote-testpy
 +
  # Generated files for scripts
  SCRIPT_SH_GEN = $(patsubst %.sh,%,$(SCRIPT_SH))
  SCRIPT_PERL_GEN = $(patsubst %.perl,%,$(SCRIPT_PERL))
  SCRIPT_PYTHON_GEN = $(patsubst %.py,%,$(SCRIPT_PYTHON))
  
 +SCRIPT_SH_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_SH_GEN))
 +SCRIPT_PERL_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PERL_GEN))
 +SCRIPT_PYTHON_INS = $(filter-out $(NO_INSTALL),$(SCRIPT_PYTHON_GEN))
 +
  # Individual rules to allow e.g.
  # "make -C ../.. SCRIPT_PERL=contrib/foo/bar.perl build-perl-script"
  # from subdirectories like contrib/*/
@@@ -512,12 -501,12 +512,12 @@@ build-sh-script: $(SCRIPT_SH_GEN
  build-python-script: $(SCRIPT_PYTHON_GEN)
  
  .PHONY: install-perl-script install-sh-script install-python-script
 -install-sh-script: $(SCRIPT_SH_GEN)
 -      $(INSTALL) $(SCRIPT_SH_GEN) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 -install-perl-script: $(SCRIPT_PERL_GEN)
 -      $(INSTALL) $(SCRIPT_PERL_GEN) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 -install-python-script: $(SCRIPT_PYTHON_GEN)
 -      $(INSTALL) $(SCRIPT_PYTHON_GEN) '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +install-sh-script: $(SCRIPT_SH_INS)
 +      $(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +install-perl-script: $(SCRIPT_PERL_INS)
 +      $(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
 +install-python-script: $(SCRIPT_PYTHON_INS)
 +      $(INSTALL) $^ '$(DESTDIR_SQ)$(gitexec_instdir_SQ)'
  
  .PHONY: clean-perl-script clean-sh-script clean-python-script
  clean-sh-script:
@@@ -527,9 -516,9 +527,9 @@@ clean-perl-script
  clean-python-script:
        $(RM) $(SCRIPT_PYTHON_GEN)
  
 -SCRIPTS = $(SCRIPT_SH_GEN) \
 -        $(SCRIPT_PERL_GEN) \
 -        $(SCRIPT_PYTHON_GEN) \
 +SCRIPTS = $(SCRIPT_SH_INS) \
 +        $(SCRIPT_PERL_INS) \
 +        $(SCRIPT_PYTHON_INS) \
          git-instaweb
  
  ETAGS_TARGET = TAGS
@@@ -569,8 -558,6 +569,8 @@@ TEST_PROGRAMS_NEED_X += test-mergesor
  TEST_PROGRAMS_NEED_X += test-mktemp
  TEST_PROGRAMS_NEED_X += test-parse-options
  TEST_PROGRAMS_NEED_X += test-path-utils
 +TEST_PROGRAMS_NEED_X += test-prio-queue
 +TEST_PROGRAMS_NEED_X += test-read-cache
  TEST_PROGRAMS_NEED_X += test-regex
  TEST_PROGRAMS_NEED_X += test-revision-walking
  TEST_PROGRAMS_NEED_X += test-run-command
@@@ -580,6 -567,7 +580,7 @@@ TEST_PROGRAMS_NEED_X += test-sigchai
  TEST_PROGRAMS_NEED_X += test-string-list
  TEST_PROGRAMS_NEED_X += test-subprocess
  TEST_PROGRAMS_NEED_X += test-svn-fe
+ TEST_PROGRAMS_NEED_X += test-urlmatch-normalization
  TEST_PROGRAMS_NEED_X += test-wildmatch
  
  TEST_PROGRAMS = $(patsubst %,%$X,$(TEST_PROGRAMS_NEED_X))
@@@ -654,6 -642,7 +655,6 @@@ LIB_H += color.
  LIB_H += column.h
  LIB_H += commit.h
  LIB_H += compat/bswap.h
 -LIB_H += compat/cygwin.h
  LIB_H += compat/mingw.h
  LIB_H += compat/obstack.h
  LIB_H += compat/poll/poll.h
@@@ -685,8 -674,6 +686,8 @@@ LIB_H += help.
  LIB_H += http.h
  LIB_H += kwset.h
  LIB_H += levenshtein.h
 +LIB_H += line-log.h
 +LIB_H += line-range.h
  LIB_H += list-objects.h
  LIB_H += ll-merge.h
  LIB_H += log-tree.h
@@@ -696,16 -683,15 +697,16 @@@ LIB_H += merge-recursive.
  LIB_H += mergesort.h
  LIB_H += notes-cache.h
  LIB_H += notes-merge.h
 +LIB_H += notes-utils.h
  LIB_H += notes.h
  LIB_H += object.h
 -LIB_H += pack-refs.h
  LIB_H += pack-revindex.h
  LIB_H += pack.h
  LIB_H += parse-options.h
  LIB_H += patch-ids.h
  LIB_H += pathspec.h
  LIB_H += pkt-line.h
 +LIB_H += prio-queue.h
  LIB_H += progress.h
  LIB_H += prompt.h
  LIB_H += quote.h
@@@ -736,6 -722,7 +737,7 @@@ LIB_H += tree-walk.
  LIB_H += tree.h
  LIB_H += unpack-trees.h
  LIB_H += url.h
+ LIB_H += urlmatch.h
  LIB_H += userdiff.h
  LIB_H += utf8.h
  LIB_H += varint.h
@@@ -816,8 -803,6 +818,8 @@@ LIB_OBJS += hex.
  LIB_OBJS += ident.o
  LIB_OBJS += kwset.o
  LIB_OBJS += levenshtein.o
 +LIB_OBJS += line-log.o
 +LIB_OBJS += line-range.o
  LIB_OBJS += list-objects.o
  LIB_OBJS += ll-merge.o
  LIB_OBJS += lockfile.o
@@@ -832,9 -817,9 +834,9 @@@ LIB_OBJS += name-hash.
  LIB_OBJS += notes.o
  LIB_OBJS += notes-cache.o
  LIB_OBJS += notes-merge.o
 +LIB_OBJS += notes-utils.o
  LIB_OBJS += object.o
  LIB_OBJS += pack-check.o
 -LIB_OBJS += pack-refs.o
  LIB_OBJS += pack-revindex.o
  LIB_OBJS += pack-write.o
  LIB_OBJS += pager.o
@@@ -847,7 -832,6 +849,7 @@@ LIB_OBJS += pathspec.
  LIB_OBJS += pkt-line.o
  LIB_OBJS += preload-index.o
  LIB_OBJS += pretty.o
 +LIB_OBJS += prio-queue.o
  LIB_OBJS += progress.o
  LIB_OBJS += prompt.o
  LIB_OBJS += quote.o
@@@ -886,6 -870,7 +888,7 @@@ LIB_OBJS += tree.
  LIB_OBJS += tree-walk.o
  LIB_OBJS += unpack-trees.o
  LIB_OBJS += url.o
+ LIB_OBJS += urlmatch.o
  LIB_OBJS += usage.o
  LIB_OBJS += userdiff.o
  LIB_OBJS += utf8.o
@@@ -911,7 -896,6 +914,7 @@@ BUILTIN_OBJS += builtin/bundle.
  BUILTIN_OBJS += builtin/cat-file.o
  BUILTIN_OBJS += builtin/check-attr.o
  BUILTIN_OBJS += builtin/check-ignore.o
 +BUILTIN_OBJS += builtin/check-mailmap.o
  BUILTIN_OBJS += builtin/check-ref-format.o
  BUILTIN_OBJS += builtin/checkout-index.o
  BUILTIN_OBJS += builtin/checkout.o
@@@ -1073,11 -1057,6 +1076,11 @@@ ifeq ($(uname_S),Darwin
                        BASIC_LDFLAGS += -L/opt/local/lib
                endif
        endif
 +      ifndef NO_APPLE_COMMON_CRYPTO
 +              APPLE_COMMON_CRYPTO = YesPlease
 +              COMPAT_CFLAGS += -DAPPLE_COMMON_CRYPTO
 +      endif
 +      NO_REGEX = YesPlease
        PTHREAD_LIBS =
  endif
  
@@@ -1182,9 -1161,6 +1185,9 @@@ ifdef NEEDS_SSL_WITH_CRYPT
  else
        LIB_4_CRYPTO = $(OPENSSL_LINK) -lcrypto
  endif
 +ifdef APPLE_COMMON_CRYPTO
 +      LIB_4_CRYPTO += -framework Security -framework CoreFoundation
 +endif
  endif
  ifdef NEEDS_LIBICONV
        ifdef ICONVDIR
@@@ -1288,7 -1264,7 +1291,7 @@@ ifdef NO_FNMATCH_CASEFOL
        COMPAT_OBJS += compat/fnmatch/fnmatch.o
  endif
  endif
 -ifdef USE_WILDMATCH
 +ifndef NO_WILDMATCH
        COMPAT_CFLAGS += -DUSE_WILDMATCH
  endif
  ifdef NO_SETENV
@@@ -1414,17 -1390,11 +1417,17 @@@ ifdef PPC_SHA
        SHA1_HEADER = "ppc/sha1.h"
        LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
        LIB_H += ppc/sha1.h
 +else
 +ifdef APPLE_COMMON_CRYPTO
 +      COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL
 +      SHA1_HEADER = <CommonCrypto/CommonDigest.h>
  else
        SHA1_HEADER = <openssl/sha.h>
        EXTLIBS += $(LIB_4_CRYPTO)
  endif
  endif
 +endif
 +
  ifdef NO_PERL_MAKEMAKER
        export NO_PERL_MAKEMAKER
  endif
@@@ -1482,6 -1452,9 +1485,6 @@@ ifdef NO_REGE
        COMPAT_CFLAGS += -Icompat/regex
        COMPAT_OBJS += compat/regex/regex.o
  endif
 -ifdef CYGWIN_V15_WIN32API
 -      COMPAT_CFLAGS += -DCYGWIN_V15_WIN32API
 -endif
  
  ifdef USE_NED_ALLOCATOR
         COMPAT_CFLAGS += -Icompat/nedmalloc
@@@ -1496,11 -1469,6 +1499,11 @@@ ifndef NO_MSGFMT_EXTENDED_OPTION
        MSGFMT += --check --statistics
  endif
  
 +ifdef NEEDS_CLIPPED_WRITE
 +      BASIC_CFLAGS += -DNEEDS_CLIPPED_WRITE
 +      COMPAT_OBJS += compat/clipped-write.o
 +endif
 +
  ifneq (,$(XDL_FAST_HASH))
        BASIC_CFLAGS += -DXDL_FAST_HASH
  endif
@@@ -1538,7 -1506,6 +1541,7 @@@ ifndef 
        QUIET_MSGFMT   = @echo '   ' MSGFMT $@;
        QUIET_GCOV     = @echo '   ' GCOV $@;
        QUIET_SP       = @echo '   ' SP $<;
 +      QUIET_RC       = @echo '   ' RC $@;
        QUIET_SUBDIR0  = +@subdir=
        QUIET_SUBDIR1  = ;$(NO_SUBDIR) echo '   ' SUBDIR $$subdir; \
                         $(MAKE) $(PRINT_DIR) -C $$subdir
@@@ -1681,7 -1648,7 +1684,7 @@@ please_set_SHELL_PATH_to_a_more_modern_
  shell_compatibility_test: please_set_SHELL_PATH_to_a_more_modern_shell
  
  strip: $(PROGRAMS) git$X
 -      $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
 +      $(STRIP) $(STRIP_OPTS) $^
  
  ### Target-specific flags and dependencies
  
@@@ -1741,9 -1708,9 +1744,9 @@@ version.sp version.s version.o: EXTRA_C
  
  $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && \
 -      ln git$X $@ 2>/dev/null || \
 -      ln -s git$X $@ 2>/dev/null || \
 -      cp git$X $@
 +      ln $< $@ 2>/dev/null || \
 +      ln -s $< $@ 2>/dev/null || \
 +      cp $< $@
  
  common-cmds.h: ./generate-cmdlist.sh command-list.txt
  
@@@ -1784,11 -1751,6 +1787,11 @@@ $(SCRIPT_LIB) : % : %.sh GIT-SCRIPT-DEF
        $(QUIET_GEN)$(cmd_munge_script) && \
        mv $@+ $@
  
 +git.res: git.rc GIT-VERSION-FILE
 +      $(QUIET_RC)$(RC) \
 +        $(join -DMAJOR= -DMINOR= -DPATCH=, $(wordlist 1,3,$(subst -, ,$(subst ., ,$(GIT_VERSION))))) \
 +        -DGIT_VERSION="\\\"$(GIT_VERSION)\\\"" $< -o $@
 +
  ifndef NO_PERL
  $(patsubst %.perl,%,$(SCRIPT_PERL)): perl/perl.mak
  
@@@ -1813,7 -1775,7 +1816,7 @@@ $(patsubst %.perl,%,$(SCRIPT_PERL)): % 
            -e '        x' \
            -e '}' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
 -          $@.perl >$@+ && \
 +          $< >$@+ && \
        chmod +x $@+ && \
        mv $@+ $@
  
@@@ -1837,8 -1799,8 +1840,8 @@@ $(patsubst %.perl,%,$(SCRIPT_PERL)) git
  endif # NO_PERL
  
  ifndef NO_PYTHON
 -$(patsubst %.py,%,$(SCRIPT_PYTHON)): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
 -$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : %.py
 +$(SCRIPT_PYTHON_GEN): GIT-CFLAGS GIT-PREFIX GIT-PYTHON-VARS
 +$(SCRIPT_PYTHON_GEN): % : %.py
        $(QUIET_GEN)$(RM) $@ $@+ && \
        INSTLIBDIR=`MAKEFLAGS= $(MAKE) -C git_remote_helpers -s \
                --no-print-directory prefix='$(prefix_SQ)' DESTDIR='$(DESTDIR_SQ)' \
        sed -e '1s|#!.*python|#!$(PYTHON_PATH_SQ)|' \
            -e 's|\(os\.getenv("GITPYTHONLIB"\)[^)]*)|\1,"@@INSTLIBDIR@@")|' \
            -e 's|@@INSTLIBDIR@@|'"$$INSTLIBDIR"'|g' \
 -          $@.py >$@+ && \
 +          $< >$@+ && \
        chmod +x $@+ && \
        mv $@+ $@
  else # NO_PYTHON
 -$(patsubst %.py,%,$(SCRIPT_PYTHON)): % : unimplemented.sh
 +$(SCRIPT_PYTHON_GEN): % : unimplemented.sh
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1s|#!.*/sh|#!$(SHELL_PATH_SQ)|' \
            -e 's|@@REASON@@|NO_PYTHON=$(NO_PYTHON)|g' \
@@@ -2045,7 -2007,6 +2048,7 @@@ endi
  ifdef USE_NED_ALLOCATOR
  compat/nedmalloc/nedmalloc.sp compat/nedmalloc/nedmalloc.o: EXTRA_CPPFLAGS = \
        -DNDEBUG -DOVERRIDE_STRDUP -DREPLACE_SYSTEM_ALLOCATOR
 +compat/nedmalloc/nedmalloc.sp: SPARSE_FLAGS += -Wno-non-pointer-null
  endif
  
  git-%$X: %.o GIT-LDFLAGS $(GITLIBS)
@@@ -2077,13 -2038,13 +2080,13 @@@ $(REMOTE_CURL_PRIMARY): remote-curl.o h
                $(LIBS) $(CURL_LIBCURL) $(EXPAT_LIBEXPAT)
  
  $(LIB_FILE): $(LIB_OBJS)
 -      $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(LIB_OBJS)
 +      $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $^
  
  $(XDIFF_LIB): $(XDIFF_OBJS)
 -      $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(XDIFF_OBJS)
 +      $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $^
  
  $(VCSSVN_LIB): $(VCSSVN_OBJS)
 -      $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $(VCSSVN_OBJS)
 +      $(QUIET_AR)$(RM) $@ && $(AR) rcs $@ $^
  
  export DEFAULT_EDITOR DEFAULT_PAGER
  
@@@ -2201,9 -2162,6 +2204,9 @@@ GIT-BUILD-OPTIONS: FORC
        @echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@
        @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@
 +ifdef TEST_OUTPUT_DIRECTORY
 +      @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@
 +endif
  ifdef GIT_TEST_OPTS
        @echo GIT_TEST_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_OPTS)))'\' >>$@
  endif
@@@ -2243,7 -2201,6 +2246,7 @@@ endi
  test_bindir_programs := $(patsubst %,bin-wrappers/%,$(BINDIR_PROGRAMS_NEED_X) $(BINDIR_PROGRAMS_NO_X) $(TEST_PROGRAMS_NEED_X))
  
  all:: $(TEST_PROGRAMS) $(test_bindir_programs)
 +all:: $(NO_INSTALL)
  
  bin-wrappers/%: wrap-for-bin.sh
        @mkdir -p bin-wrappers
@@@ -2308,6 -2265,9 +2311,6 @@@ check: common-cmds.
                exit 1; \
        fi
  
 -remove-dashes:
 -      ./fixup-builtins $(BUILT_INS) $(PROGRAMS) $(SCRIPTS)
 -
  ### Installation rules
  
  ifneq ($(filter /%,$(firstword $(template_dir))),)
@@@ -2486,11 -2446,11 +2489,11 @@@ profile-clean
        $(RM) $(addsuffix *.gcda,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
        $(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
  
 -clean: profile-clean
 -      $(RM) *.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
 +clean: profile-clean coverage-clean
 +      $(RM) *.o *.res block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o xdiff/*.o vcs-svn/*.o \
                builtin/*.o $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
 -      $(RM) $(TEST_PROGRAMS)
 +      $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
        $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
@@@ -2567,34 -2527,29 +2570,34 @@@ check-builtins:
  
  ### Test suite coverage testing
  #
 -.PHONY: coverage coverage-clean coverage-build coverage-report
 +.PHONY: coverage coverage-clean coverage-compile coverage-test coverage-report
 +.PHONY: coverage-clean-results
  
  coverage:
 -      $(MAKE) coverage-build
 -      $(MAKE) coverage-report
 +      $(MAKE) coverage-test
 +      $(MAKE) coverage-untested-functions
  
  object_dirs := $(sort $(dir $(OBJECTS)))
 -coverage-clean:
 +coverage-clean-results:
        $(RM) $(addsuffix *.gcov,$(object_dirs))
        $(RM) $(addsuffix *.gcda,$(object_dirs))
 -      $(RM) $(addsuffix *.gcno,$(object_dirs))
        $(RM) coverage-untested-functions
        $(RM) -r cover_db/
        $(RM) -r cover_db_html/
  
 +coverage-clean: coverage-clean-results
 +      $(RM) $(addsuffix *.gcno,$(object_dirs))
 +
  COVERAGE_CFLAGS = $(CFLAGS) -O0 -ftest-coverage -fprofile-arcs
  COVERAGE_LDFLAGS = $(CFLAGS)  -O0 -lgcov
  GCOVFLAGS = --preserve-paths --branch-probabilities --all-blocks
  
 -coverage-build: coverage-clean
 +coverage-compile:
        $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" all
 +
 +coverage-test: coverage-clean-results coverage-compile
        $(MAKE) CFLAGS="$(COVERAGE_CFLAGS)" LDFLAGS="$(COVERAGE_LDFLAGS)" \
 -              -j1 test
 +              DEFAULT_TEST_TARGET=test -j1 test
  
  coverage-report:
        $(QUIET_GCOV)for dir in $(object_dirs); do \
diff --combined builtin/config.c
index 4ab9e9a550ddf06f3822cea78689a60fd802f8d2,ae199e9f75eb1d52057cdc63da30418723049f4a..fc8d8820cbd2c0fcddb69aeb3fa258cf87e96814
@@@ -2,6 -2,7 +2,7 @@@
  #include "cache.h"
  #include "color.h"
  #include "parse-options.h"
+ #include "urlmatch.h"
  
  static const char *const builtin_config_usage[] = {
        N_("git config [options]"),
@@@ -21,7 -22,6 +22,7 @@@ static char term = '\n'
  
  static int use_global_config, use_system_config, use_local_config;
  static const char *given_config_file;
 +static const char *given_config_blob;
  static int actions, types;
  static const char *get_color_slot, *get_colorbool_slot;
  static int end_null;
@@@ -42,6 -42,7 +43,7 @@@ static int respect_includes = -1
  #define ACTION_SET_ALL (1<<12)
  #define ACTION_GET_COLOR (1<<13)
  #define ACTION_GET_COLORBOOL (1<<14)
+ #define ACTION_GET_URLMATCH (1<<15)
  
  #define TYPE_BOOL (1<<0)
  #define TYPE_INT (1<<1)
  
  static struct option builtin_config_options[] = {
        OPT_GROUP(N_("Config file location")),
 -      OPT_BOOLEAN(0, "global", &use_global_config, N_("use global config file")),
 -      OPT_BOOLEAN(0, "system", &use_system_config, N_("use system config file")),
 -      OPT_BOOLEAN(0, "local", &use_local_config, N_("use repository config file")),
 +      OPT_BOOL(0, "global", &use_global_config, N_("use global config file")),
 +      OPT_BOOL(0, "system", &use_system_config, N_("use system config file")),
 +      OPT_BOOL(0, "local", &use_local_config, N_("use repository config file")),
        OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")),
 +      OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")),
        OPT_GROUP(N_("Action")),
        OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
        OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
        OPT_BIT(0, "get-regexp", &actions, N_("get values for regexp: name-regex [value-regex]"), ACTION_GET_REGEXP),
+       OPT_BIT(0, "get-urlmatch", &actions, N_("get value specific for the URL: section[.var] URL"), ACTION_GET_URLMATCH),
        OPT_BIT(0, "replace-all", &actions, N_("replace all matching variables: name value [value_regex]"), ACTION_REPLACE_ALL),
        OPT_BIT(0, "add", &actions, N_("add a new variable: name value"), ACTION_ADD),
        OPT_BIT(0, "unset", &actions, N_("remove a variable: name [value-regex]"), ACTION_UNSET),
@@@ -75,7 -76,7 +78,7 @@@
        OPT_BIT(0, "bool-or-int", &types, N_("value is --bool or --int"), TYPE_BOOL_OR_INT),
        OPT_BIT(0, "path", &types, N_("value is a path (file or directory name)"), TYPE_PATH),
        OPT_GROUP(N_("Other")),
 -      OPT_BOOLEAN('z', "null", &end_null, N_("terminate values with NUL byte")),
 +      OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
        OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")),
        OPT_END(),
  };
@@@ -102,25 -103,13 +105,13 @@@ struct strbuf_list 
        int alloc;
  };
  
- static int collect_config(const char *key_, const char *value_, void *cb)
+ static int format_config(struct strbuf *buf, const char *key_, const char *value_)
  {
-       struct strbuf_list *values = cb;
-       struct strbuf *buf;
-       char value[256];
-       const char *vptr = value;
        int must_free_vptr = 0;
        int must_print_delim = 0;
+       char value[256];
+       const char *vptr = value;
  
-       if (!use_key_regexp && strcmp(key_, key))
-               return 0;
-       if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
-               return 0;
-       if (regexp != NULL &&
-           (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
-               return 0;
-       ALLOC_GROW(values->items, values->nr + 1, values->alloc);
-       buf = &values->items[values->nr++];
        strbuf_init(buf, 0);
  
        if (show_keys) {
                must_print_delim = 1;
        }
        if (types == TYPE_INT)
-               sprintf(value, "%d", git_config_int(key_, value_?value_:""));
+               sprintf(value, "%d", git_config_int(key_, value_ ? value_ : ""));
        else if (types == TYPE_BOOL)
                vptr = git_config_bool(key_, value_) ? "true" : "false";
        else if (types == TYPE_BOOL_OR_INT) {
        strbuf_addch(buf, term);
  
        if (must_free_vptr)
-               /* If vptr must be freed, it's a pointer to a
-                * dynamically allocated buffer, it's safe to cast to
-                * const.
-               */
                free((char *)vptr);
        return 0;
  }
  
+ static int collect_config(const char *key_, const char *value_, void *cb)
+ {
+       struct strbuf_list *values = cb;
+       if (!use_key_regexp && strcmp(key_, key))
+               return 0;
+       if (use_key_regexp && regexec(key_regexp, key_, 0, NULL, 0))
+               return 0;
+       if (regexp != NULL &&
+           (do_not_match ^ !!regexec(regexp, (value_?value_:""), 0, NULL, 0)))
+               return 0;
+       ALLOC_GROW(values->items, values->nr + 1, values->alloc);
+       return format_config(&values->items[values->nr++], key_, value_);
+ }
  static int get_value(const char *key_, const char *regex_)
  {
        int ret = CONFIG_GENERIC_ERROR;
        }
  
        git_config_with_options(collect_config, &values,
 -                              given_config_file, respect_includes);
 +                              given_config_file, given_config_blob,
 +                              respect_includes);
  
        ret = !values.nr;
  
@@@ -305,8 -305,7 +308,8 @@@ static void get_color(const char *def_c
        get_color_found = 0;
        parsed_color[0] = '\0';
        git_config_with_options(git_get_color_config, NULL,
 -                              given_config_file, respect_includes);
 +                              given_config_file, given_config_blob,
 +                              respect_includes);
  
        if (!get_color_found && def_color)
                color_parse(def_color, "command line", parsed_color);
@@@ -333,10 -332,8 +336,10 @@@ static int get_colorbool(int print
  {
        get_colorbool_found = -1;
        get_diff_color_found = -1;
 +      get_color_ui_found = -1;
        git_config_with_options(git_get_colorbool_config, NULL,
 -                              given_config_file, respect_includes);
 +                              given_config_file, given_config_blob,
 +                              respect_includes);
  
        if (get_colorbool_found < 0) {
                if (!strcmp(get_colorbool_slot, "color.diff"))
                        get_colorbool_found = get_color_ui_found;
        }
  
 +      if (get_colorbool_found < 0)
 +              /* default value if none found in config */
 +              get_colorbool_found = GIT_COLOR_AUTO;
 +
        get_colorbool_found = want_color(get_colorbool_found);
  
        if (print) {
                return get_colorbool_found ? 0 : 1;
  }
  
 -                              given_config_file, respect_includes);
 +static void check_blob_write(void)
 +{
 +      if (given_config_blob)
 +              die("writing config blobs is not supported");
 +}
 +
+ struct urlmatch_current_candidate_value {
+       char value_is_null;
+       struct strbuf value;
+ };
+ static int urlmatch_collect_fn(const char *var, const char *value, void *cb)
+ {
+       struct string_list *values = cb;
+       struct string_list_item *item = string_list_insert(values, var);
+       struct urlmatch_current_candidate_value *matched = item->util;
+       if (!matched) {
+               matched = xmalloc(sizeof(*matched));
+               strbuf_init(&matched->value, 0);
+               item->util = matched;
+       } else {
+               strbuf_reset(&matched->value);
+       }
+       if (value) {
+               strbuf_addstr(&matched->value, value);
+               matched->value_is_null = 0;
+       } else {
+               matched->value_is_null = 1;
+       }
+       return 0;
+ }
+ static char *dup_downcase(const char *string)
+ {
+       char *result;
+       size_t len, i;
+       len = strlen(string);
+       result = xmalloc(len + 1);
+       for (i = 0; i < len; i++)
+               result[i] = tolower(string[i]);
+       result[i] = '\0';
+       return result;
+ }
+ static int get_urlmatch(const char *var, const char *url)
+ {
+       char *section_tail;
+       struct string_list_item *item;
+       struct urlmatch_config config = { STRING_LIST_INIT_DUP };
+       struct string_list values = STRING_LIST_INIT_DUP;
+       config.collect_fn = urlmatch_collect_fn;
+       config.cascade_fn = NULL;
+       config.cb = &values;
+       if (!url_normalize(url, &config.url))
+               die("%s", config.url.err);
+       config.section = dup_downcase(var);
+       section_tail = strchr(config.section, '.');
+       if (section_tail) {
+               *section_tail = '\0';
+               config.key = section_tail + 1;
+               show_keys = 0;
+       } else {
+               config.key = NULL;
+               show_keys = 1;
+       }
+       git_config_with_options(urlmatch_config_entry, &config,
++                              given_config_file, NULL, respect_includes);
+       for_each_string_list_item(item, &values) {
+               struct urlmatch_current_candidate_value *matched = item->util;
+               struct strbuf key = STRBUF_INIT;
+               struct strbuf buf = STRBUF_INIT;
+               strbuf_addstr(&key, item->string);
+               format_config(&buf, key.buf,
+                             matched->value_is_null ? NULL : matched->value.buf);
+               fwrite(buf.buf, 1, buf.len, stdout);
+               strbuf_release(&key);
+               strbuf_release(&buf);
+               strbuf_release(&matched->value);
+       }
+       string_list_clear(&config.vars, 1);
+       string_list_clear(&values, 1);
+       free(config.url.url);
+       free((void *)config.section);
+       return 0;
+ }
  int cmd_config(int argc, const char **argv, const char *prefix)
  {
        int nongit = !startup_info->have_repository;
                             builtin_config_usage,
                             PARSE_OPT_STOP_AT_NON_OPTION);
  
 -      if (use_global_config + use_system_config + use_local_config + !!given_config_file > 1) {
 +      if (use_global_config + use_system_config + use_local_config +
 +          !!given_config_file + !!given_config_blob > 1) {
                error("only one config file at a time.");
                usage_with_options(builtin_config_usage, builtin_config_options);
        }
                         */
                        die("$HOME not set");
  
 -              if (access_or_warn(user_config, R_OK) &&
 -                  xdg_config && !access_or_warn(xdg_config, R_OK))
 +              if (access_or_warn(user_config, R_OK, 0) &&
 +                  xdg_config && !access_or_warn(xdg_config, R_OK, 0))
                        given_config_file = xdg_config;
                else
                        given_config_file = user_config;
                check_argc(argc, 0, 0);
                if (git_config_with_options(show_all_config, NULL,
                                            given_config_file,
 +                                          given_config_blob,
                                            respect_includes) < 0) {
                        if (given_config_file)
                                die_errno("unable to read config file '%s'",
                check_argc(argc, 0, 0);
                if (!given_config_file && nongit)
                        die("not in a git directory");
 +              if (given_config_blob)
 +                      die("editing blobs is not supported");
                git_config(git_default_config, NULL);
                launch_editor(given_config_file ?
                              given_config_file : git_path("config"),
        }
        else if (actions == ACTION_SET) {
                int ret;
 +              check_blob_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
                ret = git_config_set_in_file(given_config_file, argv[0], value);
                return ret;
        }
        else if (actions == ACTION_SET_ALL) {
 +              check_blob_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
                return git_config_set_multivar_in_file(given_config_file,
                                                       argv[0], value, argv[2], 0);
        }
        else if (actions == ACTION_ADD) {
 +              check_blob_write();
                check_argc(argc, 2, 2);
                value = normalize_value(argv[0], argv[1]);
                return git_config_set_multivar_in_file(given_config_file,
                                                       argv[0], value, "^$", 0);
        }
        else if (actions == ACTION_REPLACE_ALL) {
 +              check_blob_write();
                check_argc(argc, 2, 3);
                value = normalize_value(argv[0], argv[1]);
                return git_config_set_multivar_in_file(given_config_file,
                check_argc(argc, 1, 2);
                return get_value(argv[0], argv[1]);
        }
+       else if (actions == ACTION_GET_URLMATCH) {
+               check_argc(argc, 2, 2);
+               return get_urlmatch(argv[0], argv[1]);
+       }
        else if (actions == ACTION_UNSET) {
 +              check_blob_write();
                check_argc(argc, 1, 2);
                if (argc == 2)
                        return git_config_set_multivar_in_file(given_config_file,
                                                      argv[0], NULL);
        }
        else if (actions == ACTION_UNSET_ALL) {
 +              check_blob_write();
                check_argc(argc, 1, 2);
                return git_config_set_multivar_in_file(given_config_file,
                                                       argv[0], NULL, argv[1], 1);
        }
        else if (actions == ACTION_RENAME_SECTION) {
                int ret;
 +              check_blob_write();
                check_argc(argc, 2, 2);
                ret = git_config_rename_section_in_file(given_config_file,
                                                        argv[0], argv[1]);
        }
        else if (actions == ACTION_REMOVE_SECTION) {
                int ret;
 +              check_blob_write();
                check_argc(argc, 1, 1);
                ret = git_config_rename_section_in_file(given_config_file,
                                                        argv[0], NULL);
diff --combined http.c
index 2fbf986231d0e23b1d2c15c8d9121f1129139097,5eda356fd93c77bf7f8fe41e9151f9e2031773db..f3e1439d58a00328ece38a6eead63309fe7f58ca
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -3,6 -3,7 +3,7 @@@
  #include "sideband.h"
  #include "run-command.h"
  #include "url.h"
+ #include "urlmatch.h"
  #include "credential.h"
  #include "version.h"
  #include "pkt-line.h"
@@@ -45,7 -46,6 +46,7 @@@ static long curl_low_speed_time = -1
  static int curl_ftp_no_epsv;
  static const char *curl_http_proxy;
  static const char *curl_cookie_file;
 +static int curl_save_cookies;
  static struct credential http_auth = CREDENTIAL_INIT;
  static int http_proactive_auth;
  static const char *user_agent;
@@@ -161,8 -161,7 +162,7 @@@ static int http_options(const char *var
        if (!strcmp("http.sslcainfo", var))
                return git_config_string(&ssl_cainfo, var, value);
        if (!strcmp("http.sslcertpasswordprotected", var)) {
-               if (git_config_bool(var, value))
-                       ssl_cert_password_required = 1;
+               ssl_cert_password_required = git_config_bool(var, value);
                return 0;
        }
        if (!strcmp("http.ssltry", var)) {
  
        if (!strcmp("http.cookiefile", var))
                return git_config_string(&curl_cookie_file, var, value);
 +      if (!strcmp("http.savecookies", var)) {
 +              curl_save_cookies = git_config_bool(var, value);
 +              return 0;
 +      }
  
        if (!strcmp("http.postbuffer", var)) {
                http_post_buffer = git_config_int(var, value);
@@@ -233,15 -228,9 +233,15 @@@ static void init_curl_http_auth(CURL *r
  #else
        {
                static struct strbuf up = STRBUF_INIT;
 -              strbuf_reset(&up);
 -              strbuf_addf(&up, "%s:%s",
 -                          http_auth.username, http_auth.password);
 +              /*
 +               * Note that we assume we only ever have a single set of
 +               * credentials in a given program run, so we do not have
 +               * to worry about updating this buffer, only setting its
 +               * initial value.
 +               */
 +              if (!up.len)
 +                      strbuf_addf(&up, "%s:%s",
 +                              http_auth.username, http_auth.password);
                curl_easy_setopt(result, CURLOPT_USERPWD, up.buf);
        }
  #endif
@@@ -346,10 -335,20 +346,20 @@@ void http_init(struct remote *remote, c
  {
        char *low_speed_limit;
        char *low_speed_time;
+       char *normalized_url;
+       struct urlmatch_config config = { STRING_LIST_INIT_DUP };
+       config.section = "http";
+       config.key = NULL;
+       config.collect_fn = http_options;
+       config.cascade_fn = git_default_config;
+       config.cb = NULL;
  
        http_is_verbose = 0;
+       normalized_url = url_normalize(url, &config.url);
  
-       git_config(http_options, NULL);
+       git_config(urlmatch_config_entry, &config);
+       free(normalized_url);
  
        curl_global_init(CURL_GLOBAL_ALL);
  
@@@ -518,8 -517,6 +528,8 @@@ struct active_request_slot *get_active_
        slot->callback_data = NULL;
        slot->callback_func = NULL;
        curl_easy_setopt(slot->curl, CURLOPT_COOKIEFILE, curl_cookie_file);
 +      if (curl_save_cookies)
 +              curl_easy_setopt(slot->curl, CURLOPT_COOKIEJAR, curl_cookie_file);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
        curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
        curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);