Merge branch 'ab/dc-sha1-loose-ends'
authorJunio C Hamano <gitster@pobox.com>
Tue, 9 Jan 2018 22:32:53 +0000 (14:32 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 9 Jan 2018 22:32:53 +0000 (14:32 -0800)
Tying loose ends for the recent integration work of
collision-detecting SHA-1 implementation.

* ab/dc-sha1-loose-ends:
Makefile: NO_OPENSSL=1 should no longer imply BLK_SHA1=1

65 files changed:
.travis.yml
Documentation/RelNotes/2.16.0.txt
Documentation/config.txt
Documentation/git-check-ref-format.txt
Documentation/git-describe.txt
Documentation/git-p4.txt
Documentation/merge-config.txt
Documentation/rev-list-options.txt
Documentation/technical/partial-clone.txt [new file with mode: 0644]
GIT-VERSION-GEN
Makefile
builtin/am.c
builtin/clone.c
builtin/describe.c
builtin/fetch.c
builtin/index-pack.c
builtin/merge.c
cache.h
ci/lib-travisci.sh
ci/print-test-failures.sh
ci/run-linux32-build.sh
ci/run-linux32-docker.sh
ci/run-windows-build.sh
contrib/completion/git-prompt.sh
diff.c
git-compat-util.h
git-gui/git-gui.sh
git-gui/lib/diff.tcl
git-p4.py
git-rebase--interactive.sh
git-rebase.sh
git-svn.perl
help.c
http.c
imap-send.c
list-objects.c
merge-recursive.c
merge.c
path.h
revision.c
revision.h
send-pack.c
sequencer.c
strbuf.c
t/Makefile
t/check-non-portable-shell.pl
t/helper/test-lazy-init-name-hash.c
t/lib-git-svn.sh
t/perf/p7519-fsmonitor.sh
t/t4045-diff-relative.sh
t/t5573-pull-verify-signatures.sh [new file with mode: 0755]
t/t5615-alternate-env.sh
t/t6044-merge-unrelated-index-changes.sh
t/t6100-rev-list-in-order.sh [new file with mode: 0755]
t/t6120-describe.sh
t/t7612-merge-verify-signatures.sh
t/t9169-git-svn-dcommit-crlf.sh [new file with mode: 0755]
t/t9807-git-p4-submit.sh
t/test-lib.sh
transport-helper.c
transport-internal.h [new file with mode: 0644]
transport.c
transport.h
version.c
version.h
index 7c9aa0557eccad0f04885ba97646d3111d0d4b27..4684b3f4f30f78d5f1d5d0f7eb5bfea9b81861ba 100644 (file)
@@ -42,6 +42,7 @@ matrix:
     - env: jobname=Linux32
       os: linux
       compiler:
+      addons:
       services:
         - docker
       before_install:
index a43b4c9e4e2ce13d89902eecb21f0e81e69d9b7d..c648c128e87e2ddc64fd0d4e6cf078d47ac9ce68 100644 (file)
@@ -127,6 +127,24 @@ UI, Workflows & Features
  * "git worktree add" learned to run the post-checkout hook, just like
    "git checkout" does, after the initial checkout.
 
+ * "git svn" has been updated to strip CRs in the commit messages, as
+   recent versions of Subversion rejects them.
+
+ * "git imap-send" did not correctly quote the folder name when
+   making a request to the server, which has been corrected.
+
+ * Error messages from "git rebase" have been somewhat cleaned up.
+
+ * Git has been taught to support an https:// URL used for http.proxy
+   when using recent versions of libcurl.
+
+ * "git merge" learned to pay attention to merge.verifySignatures
+   configuration variable and pretend as if '--verify-signatures'
+   option was given from the command line.
+
+ * "git describe" was taught to dig trees deeper to find a
+   <commit-ish>:<path> that refers to a given blob object.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -201,6 +219,12 @@ Performance, Internal Implementation, Development Support etc.
  * Assorted updates for TravisCI integration.
    (merge 4f26366679 sg/travis-fixes later to maint).
 
+ * Introduce a helper to simplify code to parse a common pattern that
+   expects either "--key" or "--key=<something>".
+
+ * "git version --build-options" learned to report the host CPU and
+   the exact commit object name the binary was built from.
+
 Also contains various documentation updates and code clean-ups.
 
 
@@ -405,6 +429,18 @@ Fixes since v2.15
    documents to install.
    (merge 65289e9dcd rb/quick-install-doc later to maint).
 
+ * Update the shell prompt script (in contrib/) to strip trailing CR
+   from strings read from various "state" files.
+   (merge 041fe8fc83 ra/prompt-eread-fix later to maint).
+
+ * "git merge -s recursive" did not correctly abort when the index is
+   dirty, if the merged tree happened to be the same as the current
+   HEAD, which has been fixed.
+
+ * Bytes with high-bit set were encoded incorrectly and made
+   credential helper fail.
+   (merge 4c267f2ae3 jd/fix-strbuf-add-urlencode-bytes later to maint).
+
  * Other minor doc, test and build updates and code cleanups.
    (merge 1a1fc2d5b5 rd/man-prune-progress later to maint).
    (merge 0ba014035a rd/man-reflog-add-n later to maint).
@@ -425,3 +461,9 @@ Fixes since v2.15
    (merge d0e6326026 ot/pretty later to maint).
    (merge 44103f4197 sb/test-helper-excludes later to maint).
    (merge 170078693f jt/transport-no-more-rsync later to maint).
+   (merge c07b3adff1 bw/path-doc later to maint).
+   (merge bf9d7df950 tz/lib-git-svn-svnserve-tests later to maint).
+   (merge dec366c9a8 sr/http-sslverify-config-doc later to maint).
+   (merge 3f824e91c8 jk/test-suite-tracing later to maint).
+   (merge 1feb061701 db/doc-config-section-names-with-bs later to maint).
+   (merge 74dea0e13c jh/memihash-opt later to maint).
index 7814fb904415bcb3b520367702c6f5145cac6336..0e25b2c92b309330f27ce562e0f21f4e10179879 100644 (file)
@@ -41,11 +41,13 @@ in the section header, like in the example below:
 --------
 
 Subsection names are case sensitive and can contain any characters except
-newline (doublequote `"` and backslash can be included by escaping them
-as `\"` and `\\`, respectively).  Section headers cannot span multiple
-lines.  Variables may belong directly to a section or to a given subsection.
-You can have `[section]` if you have `[section "subsection"]`, but you
-don't need to.
+newline and the null byte. Doublequote `"` and backslash can be included
+by escaping them as `\"` and `\\`, respectively. Backslashes preceding
+other characters are dropped when reading; for example, `\t` is read as
+`t` and `\0` is read as `0` Section headers cannot span multiple lines.
+Variables may belong directly to a section or to a given subsection. You
+can have `[section]` if you have `[section "subsection"]`, but you don't
+need to.
 
 There is also a deprecated `[section.subsection]` syntax. With this
 syntax, the subsection name is converted to lower-case and is also
@@ -1968,8 +1970,8 @@ empty string.
 
 http.sslVerify::
        Whether to verify the SSL certificate when fetching or pushing
-       over HTTPS. Can be overridden by the `GIT_SSL_NO_VERIFY` environment
-       variable.
+       over HTTPS. Defaults to true. Can be overridden by the
+       `GIT_SSL_NO_VERIFY` environment variable.
 
 http.sslCert::
        File containing the SSL certificate when fetching or pushing
index cf0a0b7df28e560a243805618bf9e5b093354eeb..d9de9925856d79784441bc6d66623f533b844396 100644 (file)
@@ -79,16 +79,21 @@ reference name expressions (see linkgit:gitrevisions[7]):
 
 With the `--branch` option, the command takes a name and checks if
 it can be used as a valid branch name (e.g. when creating a new
-branch).  The rule `git check-ref-format --branch $name` implements
+branch). But be cautious when using the
+previous checkout syntax that may refer to a detached HEAD state.
+The rule `git check-ref-format --branch $name` implements
 may be stricter than what `git check-ref-format refs/heads/$name`
 says (e.g. a dash may appear at the beginning of a ref component,
 but it is explicitly forbidden at the beginning of a branch name).
 When run with `--branch` option in a repository, the input is first
-expanded for the ``previous branch syntax''
-`@{-n}`.  For example, `@{-1}` is a way to refer the last branch you
-were on.  This option should be used by porcelains to accept this
-syntax anywhere a branch name is expected, so they can act as if you
-typed the branch name.
+expanded for the ``previous checkout syntax''
+`@{-n}`.  For example, `@{-1}` is a way to refer the last thing that
+was checked out using "git checkout" operation. This option should be
+used by porcelains to accept this syntax anywhere a branch name is
+expected, so they can act as if you typed the branch name. As an
+exception note that, the ``previous checkout operation'' might result
+in a commit object name when the N-th last thing checked out was not
+a branch.
 
 OPTIONS
 -------
@@ -116,7 +121,7 @@ OPTIONS
 EXAMPLES
 --------
 
-* Print the name of the previous branch:
+* Print the name of the previous thing checked out:
 +
 ------------
 $ git check-ref-format --branch @{-1}
index c924c945ba8b79e578ddbb7598aa29fabcdf5887..e027fb8c4b8269bb2266ca98f87c9c18ca45fad3 100644 (file)
@@ -3,14 +3,14 @@ git-describe(1)
 
 NAME
 ----
-git-describe - Describe a commit using the most recent tag reachable from it
-
+git-describe - Give an object a human readable name based on an available ref
 
 SYNOPSIS
 --------
 [verse]
 'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] [<commit-ish>...]
 'git describe' [--all] [--tags] [--contains] [--abbrev=<n>] --dirty[=<mark>]
+'git describe' <blob>
 
 DESCRIPTION
 -----------
@@ -24,6 +24,12 @@ By default (without --all or --tags) `git describe` only shows
 annotated tags.  For more information about creating annotated tags
 see the -a and -s options to linkgit:git-tag[1].
 
+If the given object refers to a blob, it will be described
+as `<commit-ish>:<path>`, such that the blob can be found
+at `<path>` in the `<commit-ish>`, which itself describes the
+first commit in which this blob occurs in a reverse revision walk
+from HEAD.
+
 OPTIONS
 -------
 <commit-ish>...::
@@ -186,6 +192,14 @@ selected and output.  Here fewest commits different is defined as
 the number of commits which would be shown by `git log tag..input`
 will be the smallest number of commits possible.
 
+BUGS
+----
+
+Tree objects as well as tag objects not pointing at commits, cannot be described.
+When describing blobs, the lightweight tags pointing at blobs are ignored,
+but the blob is still described as <committ-ish>:<path> despite the lightweight
+tag being favorable.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 7436c64a95616d84af9922958e4ccacd0c28519a..d8c8f11c9f2dc94a2f4d46445e33c4f1494ab39e 100644 (file)
@@ -157,6 +157,12 @@ The p4 changes will be created as the user invoking 'git p4 submit'. The
 according to the author of the Git commit.  This option requires admin
 privileges in p4, which can be granted using 'p4 protect'.
 
+To shelve changes instead of submitting, use `--shelve` and `--update-shelve`:
+
+----
+$ git p4 submit --shelve
+$ git p4 submit --update-shelve 1234 --update-shelve 2345
+----
 
 OPTIONS
 -------
@@ -310,7 +316,7 @@ These options can be used to modify 'git p4 submit' behavior.
 
 --update-shelve CHANGELIST::
        Update an existing shelved changelist with this commit. Implies
-       --shelve.
+       --shelve. Repeat for multiple shelved changelists.
 
 --conflict=(ask|skip|quit)::
        Conflicts can occur when applying a commit to p4.  When this
index df3ea3779be369ce0f935cfac945e79f08ff4b5a..12b6bbf591ee801e0723f59db0e0abe7dc88ec73 100644 (file)
@@ -26,6 +26,10 @@ merge.ff::
        allowed (equivalent to giving the `--ff-only` option from the
        command line).
 
+merge.verifySignatures::
+       If true, this is equivalent to the --verify-signatures command
+       line option. See linkgit:git-merge[1] for details.
+
 include::fmt-merge-msg-config.txt[]
 
 merge.renameLimit::
index 8d8b7f492a44aa0b0b1d9d48fa25dc5293144087..22f5c9b43dd01c02710e44e36ee6a9dd2afc9b60 100644 (file)
@@ -686,6 +686,11 @@ ifdef::git-rev-list[]
        all object IDs which I need to download if I have the commit
        object _bar_ but not _foo_''.
 
+--in-commit-order::
+       Print tree and blob ids in order of the commits. The tree
+       and blob ids are printed after they are first referenced
+       by a commit.
+
 --objects-edge::
        Similar to `--objects`, but also print the IDs of excluded
        commits prefixed with a ``-'' character.  This is used by
diff --git a/Documentation/technical/partial-clone.txt b/Documentation/technical/partial-clone.txt
new file mode 100644 (file)
index 0000000..0bed247
--- /dev/null
@@ -0,0 +1,324 @@
+Partial Clone Design Notes
+==========================
+
+The "Partial Clone" feature is a performance optimization for Git that
+allows Git to function without having a complete copy of the repository.
+The goal of this work is to allow Git better handle extremely large
+repositories.
+
+During clone and fetch operations, Git downloads the complete contents
+and history of the repository.  This includes all commits, trees, and
+blobs for the complete life of the repository.  For extremely large
+repositories, clones can take hours (or days) and consume 100+GiB of disk
+space.
+
+Often in these repositories there are many blobs and trees that the user
+does not need such as:
+
+  1. files outside of the user's work area in the tree.  For example, in
+     a repository with 500K directories and 3.5M files in every commit,
+     we can avoid downloading many objects if the user only needs a
+     narrow "cone" of the source tree.
+
+  2. large binary assets.  For example, in a repository where large build
+     artifacts are checked into the tree, we can avoid downloading all
+     previous versions of these non-mergeable binary assets and only
+     download versions that are actually referenced.
+
+Partial clone allows us to avoid downloading such unneeded objects *in
+advance* during clone and fetch operations and thereby reduce download
+times and disk usage.  Missing objects can later be "demand fetched"
+if/when needed.
+
+Use of partial clone requires that the user be online and the origin
+remote be available for on-demand fetching of missing objects.  This may
+or may not be problematic for the user.  For example, if the user can
+stay within the pre-selected subset of the source tree, they may not
+encounter any missing objects.  Alternatively, the user could try to
+pre-fetch various objects if they know that they are going offline.
+
+
+Non-Goals
+---------
+
+Partial clone is a mechanism to limit the number of blobs and trees downloaded
+*within* a given range of commits -- and is therefore independent of and not
+intended to conflict with existing DAG-level mechanisms to limit the set of
+requested commits (i.e. shallow clone, single branch, or fetch '<refspec>').
+
+
+Design Overview
+---------------
+
+Partial clone logically consists of the following parts:
+
+- A mechanism for the client to describe unneeded or unwanted objects to
+  the server.
+
+- A mechanism for the server to omit such unwanted objects from packfiles
+  sent to the client.
+
+- A mechanism for the client to gracefully handle missing objects (that
+  were previously omitted by the server).
+
+- A mechanism for the client to backfill missing objects as needed.
+
+
+Design Details
+--------------
+
+- A new pack-protocol capability "filter" is added to the fetch-pack and
+  upload-pack negotiation.
+
+  This uses the existing capability discovery mechanism.
+  See "filter" in Documentation/technical/pack-protocol.txt.
+
+- Clients pass a "filter-spec" to clone and fetch which is passed to the
+  server to request filtering during packfile construction.
+
+  There are various filters available to accommodate different situations.
+  See "--filter=<filter-spec>" in Documentation/rev-list-options.txt.
+
+- On the server pack-objects applies the requested filter-spec as it
+  creates "filtered" packfiles for the client.
+
+  These filtered packfiles are *incomplete* in the traditional sense because
+  they may contain objects that reference objects not contained in the
+  packfile and that the client doesn't already have.  For example, the
+  filtered packfile may contain trees or tags that reference missing blobs
+  or commits that reference missing trees.
+
+- On the client these incomplete packfiles are marked as "promisor packfiles"
+  and treated differently by various commands.
+
+- On the client a repository extension is added to the local config to
+  prevent older versions of git from failing mid-operation because of
+  missing objects that they cannot handle.
+  See "extensions.partialClone" in Documentation/technical/repository-version.txt"
+
+
+Handling Missing Objects
+------------------------
+
+- An object may be missing due to a partial clone or fetch, or missing due
+  to repository corruption.  To differentiate these cases, the local
+  repository specially indicates such filtered packfiles obtained from the
+  promisor remote as "promisor packfiles".
+
+  These promisor packfiles consist of a "<name>.promisor" file with
+  arbitrary contents (like the "<name>.keep" files), in addition to
+  their "<name>.pack" and "<name>.idx" files.
+
+- The local repository considers a "promisor object" to be an object that
+  it knows (to the best of its ability) that the promisor remote has promised
+  that it has, either because the local repository has that object in one of
+  its promisor packfiles, or because another promisor object refers to it.
+
+  When Git encounters a missing object, Git can see if it a promisor object
+  and handle it appropriately.  If not, Git can report a corruption.
+
+  This means that there is no need for the client to explicitly maintain an
+  expensive-to-modify list of missing objects.[a]
+
+- Since almost all Git code currently expects any referenced object to be
+  present locally and because we do not want to force every command to do
+  a dry-run first, a fallback mechanism is added to allow Git to attempt
+  to dynamically fetch missing objects from the promisor remote.
+
+  When the normal object lookup fails to find an object, Git invokes
+  fetch-object to try to get the object from the server and then retry
+  the object lookup.  This allows objects to be "faulted in" without
+  complicated prediction algorithms.
+
+  For efficiency reasons, no check as to whether the missing object is
+  actually a promisor object is performed.
+
+  Dynamic object fetching tends to be slow as objects are fetched one at
+  a time.
+
+- `checkout` (and any other command using `unpack-trees`) has been taught
+  to bulk pre-fetch all required missing blobs in a single batch.
+
+- `rev-list` has been taught to print missing objects.
+
+  This can be used by other commands to bulk prefetch objects.
+  For example, a "git log -p A..B" may internally want to first do
+  something like "git rev-list --objects --quiet --missing=print A..B"
+  and prefetch those objects in bulk.
+
+- `fsck` has been updated to be fully aware of promisor objects.
+
+- `repack` in GC has been updated to not touch promisor packfiles at all,
+  and to only repack other objects.
+
+- The global variable "fetch_if_missing" is used to control whether an
+  object lookup will attempt to dynamically fetch a missing object or
+  report an error.
+
+  We are not happy with this global variable and would like to remove it,
+  but that requires significant refactoring of the object code to pass an
+  additional flag.  We hope that concurrent efforts to add an ODB API can
+  encompass this.
+
+
+Fetching Missing Objects
+------------------------
+
+- Fetching of objects is done using the existing transport mechanism using
+  transport_fetch_refs(), setting a new transport option
+  TRANS_OPT_NO_DEPENDENTS to indicate that only the objects themselves are
+  desired, not any object that they refer to.
+
+  Because some transports invoke fetch_pack() in the same process, fetch_pack()
+  has been updated to not use any object flags when the corresponding argument
+  (no_dependents) is set.
+
+- The local repository sends a request with the hashes of all requested
+  objects as "want" lines, and does not perform any packfile negotiation.
+  It then receives a packfile.
+
+- Because we are reusing the existing fetch-pack mechanism, fetching
+  currently fetches all objects referred to by the requested objects, even
+  though they are not necessary.
+
+
+Current Limitations
+-------------------
+
+- The remote used for a partial clone (or the first partial fetch
+  following a regular clone) is marked as the "promisor remote".
+
+  We are currently limited to a single promisor remote and only that
+  remote may be used for subsequent partial fetches.
+
+  We accept this limitation because we believe initial users of this
+  feature will be using it on repositories with a strong single central
+  server.
+
+- Dynamic object fetching will only ask the promisor remote for missing
+  objects.  We assume that the promisor remote has a complete view of the
+  repository and can satisfy all such requests.
+
+- Repack essentially treats promisor and non-promisor packfiles as 2
+  distinct partitions and does not mix them.  Repack currently only works
+  on non-promisor packfiles and loose objects.
+
+- Dynamic object fetching invokes fetch-pack once *for each item*
+  because most algorithms stumble upon a missing object and need to have
+  it resolved before continuing their work.  This may incur significant
+  overhead -- and multiple authentication requests -- if many objects are
+  needed.
+
+- Dynamic object fetching currently uses the existing pack protocol V0
+  which means that each object is requested via fetch-pack.  The server
+  will send a full set of info/refs when the connection is established.
+  If there are large number of refs, this may incur significant overhead.
+
+
+Future Work
+-----------
+
+- Allow more than one promisor remote and define a strategy for fetching
+  missing objects from specific promisor remotes or of iterating over the
+  set of promisor remotes until a missing object is found.
+
+  A user might want to have multiple geographically-close cache servers
+  for fetching missing blobs while continuing to do filtered `git-fetch`
+  commands from the central server, for example.
+
+  Or the user might want to work in a triangular work flow with multiple
+  promisor remotes that each have an incomplete view of the repository.
+
+- Allow repack to work on promisor packfiles (while keeping them distinct
+  from non-promisor packfiles).
+
+- Allow non-pathname-based filters to make use of packfile bitmaps (when
+  present).  This was just an omission during the initial implementation.
+
+- Investigate use of a long-running process to dynamically fetch a series
+  of objects, such as proposed in [5,6] to reduce process startup and
+  overhead costs.
+
+  It would be nice if pack protocol V2 could allow that long-running
+  process to make a series of requests over a single long-running
+  connection.
+
+- Investigate pack protocol V2 to avoid the info/refs broadcast on
+  each connection with the server to dynamically fetch missing objects.
+
+- Investigate the need to handle loose promisor objects.
+
+  Objects in promisor packfiles are allowed to reference missing objects
+  that can be dynamically fetched from the server.  An assumption was
+  made that loose objects are only created locally and therefore should
+  not reference a missing object.  We may need to revisit that assumption
+  if, for example, we dynamically fetch a missing tree and store it as a
+  loose object rather than a single object packfile.
+
+  This does not necessarily mean we need to mark loose objects as promisor;
+  it may be sufficient to relax the object lookup or is-promisor functions.
+
+
+Non-Tasks
+---------
+
+- Every time the subject of "demand loading blobs" comes up it seems
+  that someone suggests that the server be allowed to "guess" and send
+  additional objects that may be related to the requested objects.
+
+  No work has gone into actually doing that; we're just documenting that
+  it is a common suggestion.  We're not sure how it would work and have
+  no plans to work on it.
+
+  It is valid for the server to send more objects than requested (even
+  for a dynamic object fetch), but we are not building on that.
+
+
+Footnotes
+---------
+
+[a] expensive-to-modify list of missing objects:  Earlier in the design of
+    partial clone we discussed the need for a single list of missing objects.
+    This would essentially be a sorted linear list of OIDs that the were
+    omitted by the server during a clone or subsequent fetches.
+
+    This file would need to be loaded into memory on every object lookup.
+    It would need to be read, updated, and re-written (like the .git/index)
+    on every explicit "git fetch" command *and* on any dynamic object fetch.
+
+    The cost to read, update, and write this file could add significant
+    overhead to every command if there are many missing objects.  For example,
+    if there are 100M missing blobs, this file would be at least 2GiB on disk.
+
+    With the "promisor" concept, we *infer* a missing object based upon the
+    type of packfile that references it.
+
+
+Related Links
+-------------
+[0] https://bugs.chromium.org/p/git/issues/detail?id=2
+    Chromium work item for: Partial Clone
+
+[1] https://public-inbox.org/git/20170113155253.1644-1-benpeart@microsoft.com/
+    Subject: [RFC] Add support for downloading blobs on demand
+    Date: Fri, 13 Jan 2017 10:52:53 -0500
+
+[2] https://public-inbox.org/git/cover.1506714999.git.jonathantanmy@google.com/
+    Subject: [PATCH 00/18] Partial clone (from clone to lazy fetch in 18 patches)
+    Date: Fri, 29 Sep 2017 13:11:36 -0700
+
+[3] https://public-inbox.org/git/20170426221346.25337-1-jonathantanmy@google.com/
+    Subject: Proposal for missing blob support in Git repos
+    Date: Wed, 26 Apr 2017 15:13:46 -0700
+
+[4] https://public-inbox.org/git/1488999039-37631-1-git-send-email-git@jeffhostetler.com/
+    Subject: [PATCH 00/10] RFC Partial Clone and Fetch
+    Date: Wed,  8 Mar 2017 18:50:29 +0000
+
+[5] https://public-inbox.org/git/20170505152802.6724-1-benpeart@microsoft.com/
+    Subject: [PATCH v7 00/10] refactor the filter process code into a reusable module
+    Date: Fri,  5 May 2017 11:27:52 -0400
+
+[6] https://public-inbox.org/git/20170714132651.170708-1-benpeart@microsoft.com/
+    Subject: [RFC/PATCH v2 0/1] Add support for downloading blobs on demand
+    Date: Fri, 14 Jul 2017 09:26:50 -0400
index c1906f0fd1588ebb296f80b478f138dab4dc7c5a..e55ac4cb6821ee8492f7c1c18ae78816c870d6f0 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.15.GIT
+DEF_VER=v2.16.0-rc1
 
 LF='
 '
index 7e1da28b6f323bc22c64cfcdb75b539a5d7beff5..1a9b23b6793f91072760affefac3851ca457a2f5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -424,6 +424,13 @@ all::
 #
 # to say "export LESS=FRX (and LV=-c) if the environment variable
 # LESS (and LV) is not set, respectively".
+#
+# Define TEST_SHELL_PATH if you want to use a shell besides SHELL_PATH for
+# running the test scripts (e.g., bash has better support for "set -x"
+# tracing).
+#
+# When cross-compiling, define HOST_CPU as the canonical name of the CPU on
+# which the built Git will run (for instance "x86_64").
 
 GIT-VERSION-FILE: FORCE
        @$(SHELL_PATH) ./GIT-VERSION-GEN
@@ -729,6 +736,8 @@ endif
 export PERL_PATH
 export PYTHON_PATH
 
+TEST_SHELL_PATH = $(SHELL_PATH)
+
 LIB_FILE = libgit.a
 XDIFF_LIB = xdiff/lib.a
 VCSSVN_LIB = vcs-svn/lib.a
@@ -1098,6 +1107,12 @@ else
 BROKEN_PATH_FIX = '/^\# @@BROKEN_PATH_FIX@@$$/d'
 endif
 
+ifeq (,$(HOST_CPU))
+       BASIC_CFLAGS += -DGIT_HOST_CPU="\"$(firstword $(subst -, ,$(uname_M)))\""
+else
+       BASIC_CFLAGS += -DGIT_HOST_CPU="\"$(HOST_CPU)\""
+endif
+
 ifneq (,$(INLINE))
        BASIC_CFLAGS += -Dinline=$(INLINE)
 endif
@@ -1727,6 +1742,7 @@ prefix_SQ = $(subst ','\'',$(prefix))
 gitwebdir_SQ = $(subst ','\'',$(gitwebdir))
 
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+TEST_SHELL_PATH_SQ = $(subst ','\'',$(TEST_SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 PYTHON_PATH_SQ = $(subst ','\'',$(PYTHON_PATH))
 TCLTK_PATH_SQ = $(subst ','\'',$(TCLTK_PATH))
@@ -1895,7 +1911,9 @@ builtin/help.sp builtin/help.s builtin/help.o: EXTRA_CPPFLAGS = \
 version.sp version.s version.o: GIT-VERSION-FILE GIT-USER-AGENT
 version.sp version.s version.o: EXTRA_CPPFLAGS = \
        '-DGIT_VERSION="$(GIT_VERSION)"' \
-       '-DGIT_USER_AGENT=$(GIT_USER_AGENT_CQ_SQ)'
+       '-DGIT_USER_AGENT=$(GIT_USER_AGENT_CQ_SQ)' \
+       '-DGIT_BUILT_FROM_COMMIT="$(shell GIT_CEILING_DIRECTORIES=\"$(CURDIR)/..\" \
+               git rev-parse -q --verify HEAD || :)"'
 
 $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && \
@@ -2357,6 +2375,7 @@ GIT-LDFLAGS: FORCE
 # and the first level quoting from the shell that runs "echo".
 GIT-BUILD-OPTIONS: FORCE
        @echo SHELL_PATH=\''$(subst ','\'',$(SHELL_PATH_SQ))'\' >$@+
+       @echo TEST_SHELL_PATH=\''$(subst ','\'',$(TEST_SHELL_PATH_SQ))'\' >>$@+
        @echo PERL_PATH=\''$(subst ','\'',$(PERL_PATH_SQ))'\' >>$@+
        @echo DIFF=\''$(subst ','\'',$(subst ','\'',$(DIFF)))'\' >>$@+
        @echo PYTHON_PATH=\''$(subst ','\'',$(PYTHON_PATH_SQ))'\' >>$@+
index 06b824518fbdfd883e1905eaafdb0001b87f91a0..acfe9d3c8cd6dbd8362c17d55dc036ce6682ce8a 100644 (file)
@@ -1146,43 +1146,6 @@ static void refresh_and_write_cache(void)
                die(_("unable to write index file"));
 }
 
-/**
- * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
- * branch, returns 1 if there are entries in the index, 0 otherwise. If an
- * strbuf is provided, the space-separated list of files that differ will be
- * appended to it.
- */
-static int index_has_changes(struct strbuf *sb)
-{
-       struct object_id head;
-       int i;
-
-       if (!get_oid_tree("HEAD", &head)) {
-               struct diff_options opt;
-
-               diff_setup(&opt);
-               opt.flags.exit_with_status = 1;
-               if (!sb)
-                       opt.flags.quick = 1;
-               do_diff_cache(&head, &opt);
-               diffcore_std(&opt);
-               for (i = 0; sb && i < diff_queued_diff.nr; i++) {
-                       if (i)
-                               strbuf_addch(sb, ' ');
-                       strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);
-               }
-               diff_flush(&opt);
-               return opt.flags.has_changes != 0;
-       } else {
-               for (i = 0; sb && i < active_nr; i++) {
-                       if (i)
-                               strbuf_addch(sb, ' ');
-                       strbuf_addstr(sb, active_cache[i]->name);
-               }
-               return !!active_nr;
-       }
-}
-
 /**
  * Dies with a user-friendly message on how to proceed after resolving the
  * problem. This message can be overridden with state->resolvemsg.
index 6ad0ab3fa40af1761482f5fc78b4b1c401f9717c..2da71db10752bf95647052dcd5b7c4b28e7a50ac 100644 (file)
@@ -1083,9 +1083,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                warning(_("--local is ignored"));
        transport->cloning = 1;
 
-       if (!transport->get_refs_list || (!is_local && !transport->fetch))
-               die(_("Don't know how to clone %s"), transport->url);
-
        transport_set_option(transport, TRANS_OPT_KEEP, "yes");
 
        if (option_depth)
index e14e162ef6ef829072f01f89ab2c797256a6861d..3b0b204b1e495626f3490d62f281291ee039ba42 100644 (file)
@@ -3,6 +3,7 @@
 #include "lockfile.h"
 #include "commit.h"
 #include "tag.h"
+#include "blob.h"
 #include "refs.h"
 #include "builtin.h"
 #include "exec_cmd.h"
@@ -12,6 +13,8 @@
 #include "hashmap.h"
 #include "argv-array.h"
 #include "run-command.h"
+#include "revision.h"
+#include "list-objects.h"
 
 #define MAX_TAGS       (FLAG_BITS - 1)
 
@@ -256,7 +259,7 @@ static unsigned long finish_depth_computation(
        return seen_commits;
 }
 
-static void display_name(struct commit_name *n)
+static void append_name(struct commit_name *n, struct strbuf *dst)
 {
        if (n->prio == 2 && !n->tag) {
                n->tag = lookup_tag(&n->oid);
@@ -272,19 +275,18 @@ static void display_name(struct commit_name *n)
        }
 
        if (n->tag)
-               printf("%s", n->tag->tag);
+               strbuf_addstr(dst, n->tag->tag);
        else
-               printf("%s", n->path);
+               strbuf_addstr(dst, n->path);
 }
 
-static void show_suffix(int depth, const struct object_id *oid)
+static void append_suffix(int depth, const struct object_id *oid, struct strbuf *dst)
 {
-       printf("-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev));
+       strbuf_addf(dst, "-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev));
 }
 
-static void describe(const char *arg, int last_one)
+static void describe_commit(struct object_id *oid, struct strbuf *dst)
 {
-       struct object_id oid;
        struct commit *cmit, *gave_up_on = NULL;
        struct commit_list *list;
        struct commit_name *n;
@@ -293,30 +295,25 @@ static void describe(const char *arg, int last_one)
        unsigned long seen_commits = 0;
        unsigned int unannotated_cnt = 0;
 
-       if (get_oid(arg, &oid))
-               die(_("Not a valid object name %s"), arg);
-       cmit = lookup_commit_reference(&oid);
-       if (!cmit)
-               die(_("%s is not a valid '%s' object"), arg, commit_type);
+       cmit = lookup_commit_reference(oid);
 
        n = find_commit_name(&cmit->object.oid);
        if (n && (tags || all || n->prio == 2)) {
                /*
                 * Exact match to an existing ref.
                 */
-               display_name(n);
+               append_name(n, dst);
                if (longformat)
-                       show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid);
+                       append_suffix(0, n->tag ? &n->tag->tagged->oid : oid, dst);
                if (suffix)
-                       printf("%s", suffix);
-               printf("\n");
+                       strbuf_addstr(dst, suffix);
                return;
        }
 
        if (!max_candidates)
                die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid));
        if (debug)
-               fprintf(stderr, _("searching to describe %s\n"), arg);
+               fprintf(stderr, _("No exact match on refs or tags, searching to describe\n"));
 
        if (!have_util) {
                struct hashmap_iter iter;
@@ -381,22 +378,21 @@ static void describe(const char *arg, int last_one)
        }
 
        if (!match_cnt) {
-               struct object_id *oid = &cmit->object.oid;
+               struct object_id *cmit_oid = &cmit->object.oid;
                if (always) {
-                       printf("%s", find_unique_abbrev(oid->hash, abbrev));
+                       strbuf_addstr(dst, find_unique_abbrev(cmit_oid->hash, abbrev));
                        if (suffix)
-                               printf("%s", suffix);
-                       printf("\n");
+                               strbuf_addstr(dst, suffix);
                        return;
                }
                if (unannotated_cnt)
                        die(_("No annotated tags can describe '%s'.\n"
                            "However, there were unannotated tags: try --tags."),
-                           oid_to_hex(oid));
+                           oid_to_hex(cmit_oid));
                else
                        die(_("No tags can describe '%s'.\n"
                            "Try --always, or create some tags."),
-                           oid_to_hex(oid));
+                           oid_to_hex(cmit_oid));
        }
 
        QSORT(all_matches, match_cnt, compare_pt);
@@ -434,15 +430,86 @@ static void describe(const char *arg, int last_one)
                }
        }
 
-       display_name(all_matches[0].name);
+       append_name(all_matches[0].name, dst);
        if (abbrev)
-               show_suffix(all_matches[0].depth, &cmit->object.oid);
+               append_suffix(all_matches[0].depth, &cmit->object.oid, dst);
        if (suffix)
-               printf("%s", suffix);
-       printf("\n");
+               strbuf_addstr(dst, suffix);
+}
+
+struct process_commit_data {
+       struct object_id current_commit;
+       struct object_id looking_for;
+       struct strbuf *dst;
+       struct rev_info *revs;
+};
+
+static void process_commit(struct commit *commit, void *data)
+{
+       struct process_commit_data *pcd = data;
+       pcd->current_commit = commit->object.oid;
+}
+
+static void process_object(struct object *obj, const char *path, void *data)
+{
+       struct process_commit_data *pcd = data;
+
+       if (!oidcmp(&pcd->looking_for, &obj->oid) && !pcd->dst->len) {
+               reset_revision_walk();
+               describe_commit(&pcd->current_commit, pcd->dst);
+               strbuf_addf(pcd->dst, ":%s", path);
+               free_commit_list(pcd->revs->commits);
+               pcd->revs->commits = NULL;
+       }
+}
+
+static void describe_blob(struct object_id oid, struct strbuf *dst)
+{
+       struct rev_info revs;
+       struct argv_array args = ARGV_ARRAY_INIT;
+       struct process_commit_data pcd = { null_oid, oid, dst, &revs};
+
+       argv_array_pushl(&args, "internal: The first arg is not parsed",
+               "--objects", "--in-commit-order", "--reverse", "HEAD",
+               NULL);
+
+       init_revisions(&revs, NULL);
+       if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1)
+               BUG("setup_revisions could not handle all args?");
+
+       if (prepare_revision_walk(&revs))
+               die("revision walk setup failed");
+
+       traverse_commit_list(&revs, process_commit, process_object, &pcd);
+       reset_revision_walk();
+}
+
+static void describe(const char *arg, int last_one)
+{
+       struct object_id oid;
+       struct commit *cmit;
+       struct strbuf sb = STRBUF_INIT;
+
+       if (debug)
+               fprintf(stderr, _("describe %s\n"), arg);
+
+       if (get_oid(arg, &oid))
+               die(_("Not a valid object name %s"), arg);
+       cmit = lookup_commit_reference_gently(&oid, 1);
+
+       if (cmit)
+               describe_commit(&oid, &sb);
+       else if (lookup_blob(&oid))
+               describe_blob(oid, &sb);
+       else
+               die(_("%s is neither a commit nor blob"), arg);
+
+       puts(sb.buf);
 
        if (!last_one)
                clear_commit_marks(cmit, -1);
+
+       strbuf_release(&sb);
 }
 
 int cmd_describe(int argc, const char **argv, const char *prefix)
index e656746ab9afd071c3dff49b6b04c878f11996fa..7bbcd26faf1fe650ccc7dbbd526db5114aac0c8f 100644 (file)
@@ -1095,9 +1095,6 @@ static int do_fetch(struct transport *transport,
                        tags = TAGS_UNSET;
        }
 
-       if (!transport->get_refs_list || !transport->fetch)
-               die(_("Don't know how to fetch from %s"), transport->url);
-
        /* if not appending, truncate FETCH_HEAD */
        if (!append && !dry_run) {
                retcode = truncate_fetch_head();
index 8ec459f522522897c9ed4def4e72b4263d4c93e8..4c51aec81f374d52e7c6a1afa73ab9dea3cf40b8 100644 (file)
@@ -1660,10 +1660,7 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                from_stdin = 1;
                        } else if (!strcmp(arg, "--fix-thin")) {
                                fix_thin_pack = 1;
-                       } else if (!strcmp(arg, "--strict")) {
-                               strict = 1;
-                               do_fsck_object = 1;
-                       } else if (skip_prefix(arg, "--strict=", &arg)) {
+                       } else if (skip_to_optional_arg(arg, "--strict", &arg)) {
                                strict = 1;
                                do_fsck_object = 1;
                                fsck_set_msg_types(&fsck_options, arg);
@@ -1679,10 +1676,8 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                verify = 1;
                                show_stat = 1;
                                stat_only = 1;
-                       } else if (!strcmp(arg, "--keep")) {
-                               keep_msg = "";
-                       } else if (starts_with(arg, "--keep=")) {
-                               keep_msg = arg + 7;
+                       } else if (skip_to_optional_arg(arg, "--keep", &keep_msg)) {
+                               ; /* nothing to do */
                        } else if (starts_with(arg, "--threads=")) {
                                char *end;
                                nr_threads = strtoul(arg+10, &end, 0);
index 612dd7bfb6c7e6ae6f636e0b2e55bfed1aff92f2..30264cfd7c1720aee814699eb3ca057e292b082d 100644 (file)
@@ -567,6 +567,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
 
        if (!strcmp(k, "merge.diffstat") || !strcmp(k, "merge.stat"))
                show_diffstat = git_config_bool(k, v);
+       else if (!strcmp(k, "merge.verifysignatures"))
+               verify_signatures = git_config_bool(k, v);
        else if (!strcmp(k, "pull.twohead"))
                return git_config_string(&pull_twohead, k, v);
        else if (!strcmp(k, "pull.octopus"))
diff --git a/cache.h b/cache.h
index a2ec8c0b55422f2b95a30d140cb2bfdf15f0721d..d8b975a5715e8a5ec7c32e176958725cba61d67a 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -644,6 +644,15 @@ extern int write_locked_index(struct index_state *, struct lock_file *lock, unsi
 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 *);
+
+/**
+ * Returns 1 if the index differs from HEAD, 0 otherwise. When on an unborn
+ * branch, returns 1 if there are entries in the index, 0 otherwise. If an
+ * strbuf is provided, the space-separated list of files that differ will be
+ * appended to it.
+ */
+extern int index_has_changes(struct strbuf *sb);
+
 extern int verify_path(const char *path);
 extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
 extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
index 331d3eb3a6914cd811448e002e8b10ed503e7263..348fe3c3c1d304f9887c24e023a90123ad43d0c8 100755 (executable)
@@ -22,7 +22,9 @@ skip_branch_tip_with_tag () {
 }
 
 # Set 'exit on error' for all CI scripts to let the caller know that
-# something went wrong
+# something went wrong.
+# Set tracing executed commands, primarily setting environment variables
+# and installing dependencies.
 set -ex
 
 skip_branch_tip_with_tag
index 8c8973cbf365ed18c1a20b81ec2bcd4a873f62b7..4f261ddc012fb162b7d414b065fe251ceff91bee 100755 (executable)
@@ -5,6 +5,15 @@
 
 . ${0%/*}/lib-travisci.sh
 
+# Tracing executed commands would produce too much noise in the loop below.
+set +x
+
+if ! ls t/test-results/*.exit >/dev/null 2>/dev/null
+then
+       echo "Build job failed before the tests could have been run"
+       exit
+fi
+
 for TEST_EXIT in t/test-results/*.exit
 do
        if [ "$(cat "$TEST_EXIT")" != "0" ]
index e30fb2cddcb59821a90a5c77496664b9a3609113..c19c50c1c9a57c14c07ffe4f5a061bb764e51aeb 100755 (executable)
@@ -6,6 +6,8 @@
 #   run-linux32-build.sh [host-user-id]
 #
 
+set -x
+
 # Update packages to the latest available versions
 linux32 --32bit i386 sh -c '
     apt update >/dev/null &&
@@ -25,6 +27,7 @@ test -z $HOST_UID || (CI_USER="ci" && useradd -u $HOST_UID $CI_USER) &&
 # Build and test
 linux32 --32bit i386 su -m -l $CI_USER -c '
     cd /usr/src/git &&
+    ln -s /tmp/travis-cache/.prove t/.prove &&
     make --jobs=2 &&
     make --quiet test
 '
index 0edf63acfafb77a061cde1225ae3543173a37cd7..3a8b2ba42aeb9a5aaba94830a45329bfe871be73 100755 (executable)
@@ -19,5 +19,6 @@ docker run \
        --env GIT_TEST_OPTS \
        --env GIT_TEST_CLONE_2GB \
        --volume "${PWD}:/usr/src/git" \
+       --volume "${HOME}/travis-cache:/tmp/travis-cache" \
        daald/ubuntu32:xenial \
        /usr/src/git/ci/run-linux32-build.sh $(id -u $USER)
index 8757b3a97c5d7598bfdb2ec1f42cbb6851bae448..86999268a72c3dc1b81383cdcfe41919dd337e15 100755 (executable)
@@ -69,6 +69,10 @@ esac
 
 echo "Visual Studio Team Services Build #${BUILD_ID}"
 
+# Tracing execued commands would produce too much noise in the waiting
+# loop below.
+set +x
+
 # Wait until build job finished
 STATUS=
 RESULT=
@@ -90,6 +94,7 @@ done
 # Print log
 echo ""
 echo ""
+set -x
 gfwci "action=log&buildId=$BUILD_ID" | cut -c 30-
 
 # Set exit code for TravisCI
index c6cbef38c2a580555dfcdc67e04f6ac507f9066c..983e419d2b7eda8f191a878a395179dcc24eb949 100644 (file)
@@ -278,11 +278,12 @@ __git_ps1_colorize_gitstring ()
        r="$c_clear$r"
 }
 
+# Helper function to read the first line of a file into a variable.
+# __git_eread requires 2 arguments, the file path and the name of the
+# variable, in that order.
 __git_eread ()
 {
-       local f="$1"
-       shift
-       test -r "$f" && read "$@" <"$f"
+       test -r "$1" && IFS=$'\r\n' read "$2" <"$1"
 }
 
 # __git_ps1 accepts 0 or 1 arguments (i.e., format string)
diff --git a/diff.c b/diff.c
index 3fb445a54d94f5f48aabed0fcfd06c378bee6719..fb22b19f09ab13032b0484e64326fef92bb0681f 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -4512,17 +4512,12 @@ int diff_opt_parse(struct diff_options *options,
                options->output_format |= DIFF_FORMAT_NUMSTAT;
        else if (!strcmp(arg, "--shortstat"))
                options->output_format |= DIFF_FORMAT_SHORTSTAT;
-       else if (!strcmp(arg, "-X") || !strcmp(arg, "--dirstat"))
-               return parse_dirstat_opt(options, "");
-       else if (skip_prefix(arg, "-X", &arg))
-               return parse_dirstat_opt(options, arg);
-       else if (skip_prefix(arg, "--dirstat=", &arg))
+       else if (skip_prefix(arg, "-X", &arg) ||
+                skip_to_optional_arg(arg, "--dirstat", &arg))
                return parse_dirstat_opt(options, arg);
        else if (!strcmp(arg, "--cumulative"))
                return parse_dirstat_opt(options, "cumulative");
-       else if (!strcmp(arg, "--dirstat-by-file"))
-               return parse_dirstat_opt(options, "files");
-       else if (skip_prefix(arg, "--dirstat-by-file=", &arg)) {
+       else if (skip_to_optional_arg(arg, "--dirstat-by-file", &arg)) {
                parse_dirstat_opt(options, "files");
                return parse_dirstat_opt(options, arg);
        }
@@ -4544,13 +4539,13 @@ int diff_opt_parse(struct diff_options *options,
                return stat_opt(options, av);
 
        /* renames options */
-       else if (starts_with(arg, "-B") || starts_with(arg, "--break-rewrites=") ||
-                !strcmp(arg, "--break-rewrites")) {
+       else if (starts_with(arg, "-B") ||
+                skip_to_optional_arg(arg, "--break-rewrites", NULL)) {
                if ((options->break_opt = diff_scoreopt_parse(arg)) == -1)
                        return error("invalid argument to -B: %s", arg+2);
        }
-       else if (starts_with(arg, "-M") || starts_with(arg, "--find-renames=") ||
-                !strcmp(arg, "--find-renames")) {
+       else if (starts_with(arg, "-M") ||
+                skip_to_optional_arg(arg, "--find-renames", NULL)) {
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
                        return error("invalid argument to -M: %s", arg+2);
                options->detect_rename = DIFF_DETECT_RENAME;
@@ -4558,8 +4553,8 @@ int diff_opt_parse(struct diff_options *options,
        else if (!strcmp(arg, "-D") || !strcmp(arg, "--irreversible-delete")) {
                options->irreversible_delete = 1;
        }
-       else if (starts_with(arg, "-C") || starts_with(arg, "--find-copies=") ||
-                !strcmp(arg, "--find-copies")) {
+       else if (starts_with(arg, "-C") ||
+                skip_to_optional_arg(arg, "--find-copies", NULL)) {
                if (options->detect_rename == DIFF_DETECT_COPY)
                        options->flags.find_copies_harder = 1;
                if ((options->rename_score = diff_scoreopt_parse(arg)) == -1)
@@ -4572,11 +4567,10 @@ int diff_opt_parse(struct diff_options *options,
                options->flags.rename_empty = 1;
        else if (!strcmp(arg, "--no-rename-empty"))
                options->flags.rename_empty = 0;
-       else if (!strcmp(arg, "--relative"))
-               options->flags.relative_name = 1;
-       else if (skip_prefix(arg, "--relative=", &arg)) {
+       else if (skip_to_optional_arg_default(arg, "--relative", &arg, NULL)) {
                options->flags.relative_name = 1;
-               options->prefix = arg;
+               if (arg)
+                       options->prefix = arg;
        }
 
        /* xdiff options */
@@ -4646,9 +4640,7 @@ int diff_opt_parse(struct diff_options *options,
        else if (!strcmp(arg, "--no-follow")) {
                options->flags.follow_renames = 0;
                options->flags.default_follow_renames = 0;
-       } else if (!strcmp(arg, "--color"))
-               options->use_color = 1;
-       else if (skip_prefix(arg, "--color=", &arg)) {
+       } else if (skip_to_optional_arg_default(arg, "--color", &arg, "always")) {
                int value = git_config_colorbool(NULL, arg);
                if (value < 0)
                        return error("option `color' expects \"always\", \"auto\", or \"never\"");
@@ -4668,14 +4660,9 @@ int diff_opt_parse(struct diff_options *options,
                if (cm < 0)
                        die("bad --color-moved argument: %s", arg);
                options->color_moved = cm;
-       } else if (!strcmp(arg, "--color-words")) {
-               options->use_color = 1;
-               options->word_diff = DIFF_WORDS_COLOR;
-       }
-       else if (skip_prefix(arg, "--color-words=", &arg)) {
+       } else if (skip_to_optional_arg_default(arg, "--color-words", &options->word_regex, NULL)) {
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
-               options->word_regex = arg;
        }
        else if (!strcmp(arg, "--word-diff")) {
                if (options->word_diff == DIFF_WORDS_NONE)
@@ -4714,15 +4701,10 @@ int diff_opt_parse(struct diff_options *options,
                options->flags.textconv_set_via_cmdline = 1;
        } else if (!strcmp(arg, "--no-textconv"))
                options->flags.allow_textconv = 0;
-       else if (!strcmp(arg, "--ignore-submodules")) {
-               options->flags.override_submodule_config = 1;
-               handle_ignore_submodules_arg(options, "all");
-       } else if (skip_prefix(arg, "--ignore-submodules=", &arg)) {
+       else if (skip_to_optional_arg_default(arg, "--ignore-submodules", &arg, "all")) {
                options->flags.override_submodule_config = 1;
                handle_ignore_submodules_arg(options, arg);
-       } else if (!strcmp(arg, "--submodule"))
-               options->submodule_format = DIFF_SUBMODULE_LOG;
-       else if (skip_prefix(arg, "--submodule=", &arg))
+       } else if (skip_to_optional_arg_default(arg, "--submodule", &arg, "log"))
                return parse_submodule_opt(options, arg);
        else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
                return parse_ws_error_highlight_opt(options, arg);
index cedad4d5818a6e5c6e1732d34cf8fef6dab36718..68b2ad531ea6f9cf1127a0e5986c523fd86b0967 100644 (file)
@@ -484,6 +484,29 @@ static inline int skip_prefix(const char *str, const char *prefix,
        return 0;
 }
 
+/*
+ * If the string "str" is the same as the string in "prefix", then the "arg"
+ * parameter is set to the "def" parameter and 1 is returned.
+ * If the string "str" begins with the string found in "prefix" and then a
+ * "=" sign, then the "arg" parameter is set to "str + strlen(prefix) + 1"
+ * (i.e., to the point in the string right after the prefix and the "=" sign),
+ * and 1 is returned.
+ *
+ * Otherwise, return 0 and leave "arg" untouched.
+ *
+ * When we accept both a "--key" and a "--key=<val>" option, this function
+ * can be used instead of !strcmp(arg, "--key") and then
+ * skip_prefix(arg, "--key=", &arg) to parse such an option.
+ */
+int skip_to_optional_arg_default(const char *str, const char *prefix,
+                                const char **arg, const char *def);
+
+static inline int skip_to_optional_arg(const char *str, const char *prefix,
+                                      const char **arg)
+{
+       return skip_to_optional_arg_default(str, prefix, arg, "");
+}
+
 /*
  * Like skip_prefix, but promises never to read past "len" bytes of the input
  * buffer, and returns the remaining number of bytes in "out" via "outlen".
index ed24aa9d2f16a4eb600fc3298ebeb931f9fb6c9c..91c00e6489305a56faed6d4e89150709ba7f2aeb 100755 (executable)
@@ -2501,9 +2501,28 @@ proc toggle_or_diff {mode w args} {
                set pos [split [$w index @$x,$y] .]
                foreach {lno col} $pos break
        } else {
+               if {$mode eq "toggle"} {
+                       if {$w eq $ui_workdir} {
+                               do_add_selection
+                               set last_clicked {}
+                               return
+                       }
+                       if {$w eq $ui_index} {
+                               do_unstage_selection
+                               set last_clicked {}
+                               return
+                       }
+               }
+
                if {$last_clicked ne {}} {
                        set lno [lindex $last_clicked 1]
                } else {
+                       if {![info exists file_lists]
+                               || ![info exists file_lists($w)]
+                               || [llength $file_lists($w)] == 0} {
+                               set last_clicked {}
+                               return
+                       }
                        set lno [expr {int([lindex [$w tag ranges in_diff] 0])}]
                }
                if {$mode eq "toggle"} {
@@ -2514,7 +2533,13 @@ proc toggle_or_diff {mode w args} {
                }
        }
 
-       set path [lindex $file_lists($w) [expr {$lno - 1}]]
+       if {![info exists file_lists]
+               || ![info exists file_lists($w)]
+               || [llength $file_lists($w)] < $lno - 1} {
+               set path {}
+       } else {
+               set path [lindex $file_lists($w) [expr {$lno - 1}]]
+       }
        if {$path eq {}} {
                set last_clicked {}
                return
index 4cae10a4c7f197aab9ae0bc145254a8d4a1bae50..68c4a6c7366f864bcfc500483c83b15860a2d185 100644 (file)
@@ -698,6 +698,7 @@ proc apply_range_or_line {x y} {
                set hh [$ui_diff get $i_l "$i_l + 1 lines"]
                set hh [lindex [split $hh ,] 0]
                set hln [lindex [split $hh -] 1]
+               set hln [lindex [split $hln " "] 0]
 
                # There is a special situation to take care of. Consider this
                # hunk:
index 76859b453afc2d5ba6897d15a680245b455f4f38..7bb9cadc69738a4348bb54ccce50d6bd35a0753f 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -1178,6 +1178,12 @@ def __init__(self):
         self.needsGit = True
         self.verbose = False
 
+    # This is required for the "append" cloneExclude action
+    def ensure_value(self, attr, value):
+        if not hasattr(self, attr) or getattr(self, attr) is None:
+            setattr(self, attr, value)
+        return getattr(self, attr)
+
 class P4UserMap:
     def __init__(self):
         self.userMapFromPerforceServer = False
@@ -1343,9 +1349,10 @@ def __init__(self):
                 optparse.make_option("--shelve", dest="shelve", action="store_true",
                                      help="Shelve instead of submit. Shelved files are reverted, "
                                      "restoring the workspace to the state before the shelve"),
-                optparse.make_option("--update-shelve", dest="update_shelve", action="store", type="int",
+                optparse.make_option("--update-shelve", dest="update_shelve", action="append", type="int",
                                      metavar="CHANGELIST",
-                                     help="update an existing shelved changelist, implies --shelve")
+                                     help="update an existing shelved changelist, implies --shelve, "
+                                           "repeat in-order for multiple shelved changelists")
         ]
         self.description = "Submit changes from git to the perforce depot."
         self.usage += " [name of git branch to submit into perforce depot]"
@@ -1354,7 +1361,7 @@ def __init__(self):
         self.preserveUser = gitConfigBool("git-p4.preserveUser")
         self.dry_run = False
         self.shelve = False
-        self.update_shelve = None
+        self.update_shelve = list()
         self.prepare_p4_only = False
         self.conflict_behavior = None
         self.isWindows = (platform.system() == "Windows")
@@ -1809,9 +1816,10 @@ def applyCommit(self, id):
             mode = filesToChangeExecBit[f]
             setP4ExecBit(f, mode)
 
-        if self.update_shelve:
-            print("all_files = %s" % str(all_files))
-            p4_reopen_in_change(self.update_shelve, all_files)
+        update_shelve = 0
+        if len(self.update_shelve) > 0:
+            update_shelve = self.update_shelve.pop(0)
+            p4_reopen_in_change(update_shelve, all_files)
 
         #
         # Build p4 change description, starting with the contents
@@ -1821,7 +1829,7 @@ def applyCommit(self, id):
         logMessage = logMessage.strip()
         (logMessage, jobs) = self.separate_jobs_from_description(logMessage)
 
-        template = self.prepareSubmitTemplate(self.update_shelve)
+        template = self.prepareSubmitTemplate(update_shelve)
         submitTemplate = self.prepareLogMessage(template, logMessage, jobs)
 
         if self.preserveUser:
@@ -1894,7 +1902,7 @@ def applyCommit(self, id):
                     message = message.replace("\r\n", "\n")
                 submitTemplate = message[:message.index(separatorLine)]
 
-                if self.update_shelve:
+                if update_shelve:
                     p4_write_pipe(['shelve', '-r', '-i'], submitTemplate)
                 elif self.shelve:
                     p4_write_pipe(['shelve', '-i'], submitTemplate)
@@ -2012,6 +2020,10 @@ def run(self, args):
         else:
             return False
 
+        for i in self.update_shelve:
+            if i <= 0:
+                sys.exit("invalid changelist %d" % i)
+
         if self.master:
             allowSubmit = gitConfig("git-p4.allowSubmit")
             if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
@@ -2022,7 +2034,7 @@ def run(self, args):
         if len(self.origin) == 0:
             self.origin = upstream
 
-        if self.update_shelve:
+        if len(self.update_shelve) > 0:
             self.shelve = True
 
         if self.preserveUser:
@@ -2134,6 +2146,11 @@ def run(self, args):
         if gitConfigBool("git-p4.detectCopiesHarder"):
             self.diffOpts += " --find-copies-harder"
 
+        num_shelves = len(self.update_shelve)
+        if num_shelves > 0 and num_shelves != len(commits):
+            sys.exit("number of commits (%d) must match number of shelved changelist (%d)" %
+                     (len(commits), num_shelves))
+
         #
         # Apply the commits, one at a time.  On failure, ask if should
         # continue to try the rest of the patches, or quit.
@@ -2404,12 +2421,6 @@ def __init__(self):
         if gitConfig("git-p4.syncFromOrigin") == "false":
             self.syncWithOrigin = False
 
-    # This is required for the "append" cloneExclude action
-    def ensure_value(self, attr, value):
-        if not hasattr(self, attr) or getattr(self, attr) is None:
-            setattr(self, attr, value)
-        return getattr(self, attr)
-
     # Force a checkpoint in fast-import and wait for it to finish
     def checkpoint(self):
         self.gitStream.write("checkpoint\n\n")
index e3f5a0abf3c76c6e6b1e4528a20fca4c8784894b..b7f95672bd90ba04564e1446c97be887645706de 100644 (file)
@@ -893,7 +893,8 @@ fi
 if test t != "$preserve_merges"
 then
        git rebase--helper --make-script ${keep_empty:+--keep-empty} \
-               $revisions ${restrict_revision+^$restrict_revision} >"$todo"
+               $revisions ${restrict_revision+^$restrict_revision} >"$todo" ||
+       die "$(gettext "Could not generate todo list")"
 else
        format=$(git config --get rebase.instructionFormat)
        # the 'rev-list .. | sed' requires %m to parse; the instruction requires %H to parse
index 60b70f3defb5678af099e4c6bd7fe0610035a432..fd72a35c65b43537b292445b87ffb7e682cc076a 100755 (executable)
@@ -477,7 +477,7 @@ then
                ;;
        esac
        upstream=$(peel_committish "${upstream_name}") ||
-       die "$(eval_gettext "invalid upstream \$upstream_name")"
+       die "$(eval_gettext "invalid upstream '\$upstream_name'")"
        upstream_arg="$upstream_name"
 else
        if test -z "$onto"
@@ -518,7 +518,7 @@ case "$onto_name" in
 esac
 
 # If the branch to rebase is given, that is the branch we will rebase
-# $branch_name -- branch being rebased, or HEAD (already detached)
+# $branch_name -- branch/commit being rebased, or HEAD (already detached)
 # $orig_head -- commit object name of tip of the branch before rebasing
 # $head_name -- refs/heads/<that-branch> or "detached HEAD"
 switch_to=
@@ -528,15 +528,18 @@ case "$#" in
        branch_name="$1"
        switch_to="$1"
 
-       if git show-ref --verify --quiet -- "refs/heads/$1" &&
-          orig_head=$(git rev-parse -q --verify "refs/heads/$1")
+       # Is it a local branch?
+       if git show-ref --verify --quiet -- "refs/heads/$branch_name" &&
+          orig_head=$(git rev-parse -q --verify "refs/heads/$branch_name")
        then
-               head_name="refs/heads/$1"
-       elif orig_head=$(git rev-parse -q --verify "$1")
+               head_name="refs/heads/$branch_name"
+       # If not is it a valid ref (branch or commit)?
+       elif orig_head=$(git rev-parse -q --verify "$branch_name")
        then
                head_name="detached HEAD"
+
        else
-               die "$(eval_gettext "fatal: no such branch: \$branch_name")"
+               die "$(eval_gettext "fatal: no such branch/commit '\$branch_name'")"
        fi
        ;;
 0)
@@ -547,7 +550,7 @@ case "$#" in
                branch_name=$(expr "z$branch_name" : 'zrefs/heads/\(.*\)')
        else
                head_name="detached HEAD"
-               branch_name=HEAD ;# detached
+               branch_name=HEAD
        fi
        orig_head=$(git rev-parse --verify HEAD) || exit
        ;;
@@ -598,11 +601,23 @@ then
                test -z "$switch_to" ||
                GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \
                        git checkout -q "$switch_to" --
-               say "$(eval_gettext "Current branch \$branch_name is up to date.")"
+               if test "$branch_name" = "HEAD" &&
+                        ! git symbolic-ref -q HEAD
+               then
+                       say "$(eval_gettext "HEAD is up to date.")"
+               else
+                       say "$(eval_gettext "Current branch \$branch_name is up to date.")"
+               fi
                finish_rebase
                exit 0
        else
-               say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
+               if test "$branch_name" = "HEAD" &&
+                        ! git symbolic-ref -q HEAD
+               then
+                       say "$(eval_gettext "HEAD is up to date, rebase forced.")"
+               else
+                       say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
+               fi
        fi
 fi
 
index d2404184ba2edc1ac330898ca610ad5e5df8d6c9..aa242d4f4f4363736730ccfcb4c2694d5be9a670 100755 (executable)
@@ -1865,6 +1865,7 @@ sub get_commit_entry {
                        }
                }
                $msgbuf =~ s/\s+$//s;
+               $msgbuf =~ s/\r\n/\n/sg; # SVN 1.6+ disallows CRLF
                if ($Git::SVN::_add_author_from && defined($author)
                    && !$saw_from) {
                        $msgbuf .= "\n\nFrom: $author";
diff --git a/help.c b/help.c
index 88a3aeaeb9f0b3983c9cd035fd371133506a54ec..60071a9beaaedf45730385b7fc1443b402c50156 100644 (file)
--- a/help.c
+++ b/help.c
@@ -412,6 +412,12 @@ int cmd_version(int argc, const char **argv, const char *prefix)
        printf("git version %s\n", git_version_string);
 
        if (build_options) {
+               printf("cpu: %s\n", GIT_HOST_CPU);
+               if (git_built_from_commit_string[0])
+                       printf("built from commit: %s\n",
+                              git_built_from_commit_string);
+               else
+                       printf("no commit associated with this build\n");
                printf("sizeof-long: %d\n", (int)sizeof(long));
                /* NEEDSWORK: also save and output GIT-BUILD_OPTIONS? */
        }
diff --git a/http.c b/http.c
index 215bebef1bfb935355a765027fc8605fccbc3d1b..5977712712b22d9076d8cd5ccda0fe12b3057d27 100644 (file)
--- a/http.c
+++ b/http.c
@@ -865,6 +865,11 @@ static CURL *get_curl_handle(void)
                else if (starts_with(curl_http_proxy, "socks"))
                        curl_easy_setopt(result,
                                CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
+#endif
+#if LIBCURL_VERSION_NUM >= 0x073400
+               else if (starts_with(curl_http_proxy, "https"))
+                       curl_easy_setopt(result,
+                               CURLOPT_PROXYTYPE, CURLPROXY_HTTPS);
 #endif
                if (strstr(curl_http_proxy, "://"))
                        credential_from_url(&proxy_auth, curl_http_proxy);
@@ -2025,7 +2030,6 @@ int finish_http_pack_request(struct http_pack_request *preq)
        char *tmp_idx;
        size_t len;
        struct child_process ip = CHILD_PROCESS_INIT;
-       const char *ip_argv[8];
 
        close_pack_index(p);
 
@@ -2041,13 +2045,9 @@ int finish_http_pack_request(struct http_pack_request *preq)
                die("BUG: pack tmpfile does not end in .pack.temp?");
        tmp_idx = xstrfmt("%.*s.idx.temp", (int)len, preq->tmpfile);
 
-       ip_argv[0] = "index-pack";
-       ip_argv[1] = "-o";
-       ip_argv[2] = tmp_idx;
-       ip_argv[3] = preq->tmpfile;
-       ip_argv[4] = NULL;
-
-       ip.argv = ip_argv;
+       argv_array_push(&ip.args, "index-pack");
+       argv_array_pushl(&ip.args, "-o", tmp_idx, NULL);
+       argv_array_push(&ip.args, preq->tmpfile);
        ip.git_cmd = 1;
        ip.no_stdin = 1;
        ip.no_stdout = 1;
index 54e6a80fd64e16420a526461e90aed559e0bd1d6..36c7c1b4f6195b2d2f00013611a5d0aa96ecf408 100644 (file)
@@ -1412,6 +1412,7 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
 {
        CURL *curl;
        struct strbuf path = STRBUF_INIT;
+       char *uri_encoded_folder;
 
        if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
                die("curl_global_init failed");
@@ -1429,7 +1430,12 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
        strbuf_addstr(&path, server.host);
        if (!path.len || path.buf[path.len - 1] != '/')
                strbuf_addch(&path, '/');
-       strbuf_addstr(&path, server.folder);
+
+       uri_encoded_folder = curl_easy_escape(curl, server.folder, 0);
+       if (!uri_encoded_folder)
+               die("failed to encode server folder");
+       strbuf_addstr(&path, uri_encoded_folder);
+       curl_free(uri_encoded_folder);
 
        curl_easy_setopt(curl, CURLOPT_URL, path.buf);
        strbuf_release(&path);
index d9e83d05e15d36a4cba49de95ad35d7cdf54c199..0966cdc9fa8dd448aa9955a2fb5324ae8ebbf97d 100644 (file)
@@ -214,27 +214,17 @@ static void add_pending_tree(struct rev_info *revs, struct tree *tree)
        add_pending_object(revs, &tree->object, "");
 }
 
-static void do_traverse(struct rev_info *revs,
-                       show_commit_fn show_commit,
-                       show_object_fn show_object,
-                       void *show_data,
-                       filter_object_fn filter_fn,
-                       void *filter_data)
+static void traverse_trees_and_blobs(struct rev_info *revs,
+                                    struct strbuf *base,
+                                    show_object_fn show_object,
+                                    void *show_data,
+                                    filter_object_fn filter_fn,
+                                    void *filter_data)
 {
        int i;
-       struct commit *commit;
-       struct strbuf base;
 
-       strbuf_init(&base, PATH_MAX);
-       while ((commit = get_revision(revs)) != NULL) {
-               /*
-                * an uninteresting boundary commit may not have its tree
-                * parsed yet, but we are not going to show them anyway
-                */
-               if (commit->tree)
-                       add_pending_tree(revs, commit->tree);
-               show_commit(commit, show_data);
-       }
+       assert(base->len == 0);
+
        for (i = 0; i < revs->pending.nr; i++) {
                struct object_array_entry *pending = revs->pending.objects + i;
                struct object *obj = pending->item;
@@ -251,13 +241,13 @@ static void do_traverse(struct rev_info *revs,
                        path = "";
                if (obj->type == OBJ_TREE) {
                        process_tree(revs, (struct tree *)obj, show_object,
-                                    &base, path, show_data,
+                                    base, path, show_data,
                                     filter_fn, filter_data);
                        continue;
                }
                if (obj->type == OBJ_BLOB) {
                        process_blob(revs, (struct blob *)obj, show_object,
-                                    &base, path, show_data,
+                                    base, path, show_data,
                                     filter_fn, filter_data);
                        continue;
                }
@@ -265,7 +255,42 @@ static void do_traverse(struct rev_info *revs,
                    oid_to_hex(&obj->oid), name);
        }
        object_array_clear(&revs->pending);
-       strbuf_release(&base);
+}
+
+static void do_traverse(struct rev_info *revs,
+                       show_commit_fn show_commit,
+                       show_object_fn show_object,
+                       void *show_data,
+                       filter_object_fn filter_fn,
+                       void *filter_data)
+{
+       struct commit *commit;
+       struct strbuf csp; /* callee's scratch pad */
+       strbuf_init(&csp, PATH_MAX);
+
+       while ((commit = get_revision(revs)) != NULL) {
+               /*
+                * an uninteresting boundary commit may not have its tree
+                * parsed yet, but we are not going to show them anyway
+                */
+               if (commit->tree)
+                       add_pending_tree(revs, commit->tree);
+               show_commit(commit, show_data);
+
+               if (revs->tree_blobs_in_commit_order)
+                       /*
+                        * NEEDSWORK: Adding the tree and then flushing it here
+                        * needs a reallocation for each commit. Can we pass the
+                        * tree directory without allocation churn?
+                        */
+                       traverse_trees_and_blobs(revs, &csp,
+                                                show_object, show_data,
+                                                filter_fn, filter_data);
+       }
+       traverse_trees_and_blobs(revs, &csp,
+                                show_object, show_data,
+                                filter_fn, filter_data);
+       strbuf_release(&csp);
 }
 
 void traverse_commit_list(struct rev_info *revs,
index 2ecf495cc2a4a6d4544b92db1d2a8c3c0cc36f87..780f81a8bd3a729502cf2211a63135e4fba5d6cf 100644 (file)
@@ -1952,6 +1952,13 @@ int merge_trees(struct merge_options *o,
        }
 
        if (oid_eq(&common->object.oid, &merge->object.oid)) {
+               struct strbuf sb = STRBUF_INIT;
+
+               if (index_has_changes(&sb)) {
+                       err(o, _("Dirty index: cannot merge (dirty: %s)"),
+                           sb.buf);
+                       return 0;
+               }
                output(o, 0, _("Already up to date!"));
                *result = head;
                return 1;
diff --git a/merge.c b/merge.c
index e5d796c9f2d110bdcd378b361fe2052ac1dcc266..195b5787005ee8f63bc10845f6d788aa4730a61b 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -1,4 +1,6 @@
 #include "cache.h"
+#include "diff.h"
+#include "diffcore.h"
 #include "lockfile.h"
 #include "commit.h"
 #include "run-command.h"
@@ -15,6 +17,37 @@ static const char *merge_argument(struct commit *commit)
                return EMPTY_TREE_SHA1_HEX;
 }
 
+int index_has_changes(struct strbuf *sb)
+{
+       struct object_id head;
+       int i;
+
+       if (!get_oid_tree("HEAD", &head)) {
+               struct diff_options opt;
+
+               diff_setup(&opt);
+               opt.flags.exit_with_status = 1;
+               if (!sb)
+                       opt.flags.quick = 1;
+               do_diff_cache(&head, &opt);
+               diffcore_std(&opt);
+               for (i = 0; sb && i < diff_queued_diff.nr; i++) {
+                       if (i)
+                               strbuf_addch(sb, ' ');
+                       strbuf_addstr(sb, diff_queued_diff.queue[i]->two->path);
+               }
+               diff_flush(&opt);
+               return opt.flags.has_changes != 0;
+       } else {
+               for (i = 0; sb && i < active_nr; i++) {
+                       if (i)
+                               strbuf_addch(sb, ' ');
+                       strbuf_addstr(sb, active_cache[i]->name);
+               }
+               return !!active_nr;
+       }
+}
+
 int try_merge_command(const char *strategy, size_t xopts_nr,
                      const char **xopts, struct commit_list *common,
                      const char *head_arg, struct commit_list *remotes)
diff --git a/path.h b/path.h
index 9541620c79dcda31e4a1b7437a79f0f63b156523..1ccd0373c9db8460c7ad7a3f9c5465f84408fd56 100644 (file)
--- a/path.h
+++ b/path.h
 struct repository;
 
 /*
- * Return a statically allocated filename, either generically (mkpath), in
- * the repository directory (git_path), or in a submodule's repository
- * directory (git_path_submodule). In all cases, note that the result
- * may be overwritten by another call to _any_ of the functions. Consider
- * using the safer "dup" or "strbuf" formats below (in some cases, the
- * unsafe versions have already been removed).
+ * The result to all functions which return statically allocated memory may be
+ * overwritten by another call to _any_ one of these functions. Consider using
+ * the safer variants which operate on strbufs or return allocated memory.
  */
-extern const char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern const char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern const char *git_common_path(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
 
+/*
+ * Return a statically allocated path.
+ */
+extern const char *mkpath(const char *fmt, ...)
+       __attribute__((format (printf, 1, 2)));
+
+/*
+ * Return a path.
+ */
+extern char *mkpathdup(const char *fmt, ...)
+       __attribute__((format (printf, 1, 2)));
+
+/*
+ * Construct a path and place the result in the provided buffer `buf`.
+ */
 extern char *mksnpath(char *buf, size_t n, const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
-extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
-       __attribute__((format (printf, 2, 3)));
+
+/*
+ * The `git_common_path` family of functions will construct a path into a
+ * repository's common git directory, which is shared by all worktrees.
+ */
+
+/*
+ * Constructs a path into the common git directory of repository `repo` and
+ * append it in the provided buffer `sb`.
+ */
 extern void strbuf_git_common_path(struct strbuf *sb,
                                   const struct repository *repo,
                                   const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
-extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
-       __attribute__((format (printf, 2, 3)));
-extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
-                                    const char *fmt, ...)
-       __attribute__((format (printf, 3, 4)));
-extern char *git_pathdup(const char *fmt, ...)
-       __attribute__((format (printf, 1, 2)));
-extern char *mkpathdup(const char *fmt, ...)
+
+/*
+ * Return a statically allocated path into the main repository's
+ * (the_repository) common git directory.
+ */
+extern const char *git_common_path(const char *fmt, ...)
        __attribute__((format (printf, 1, 2)));
-extern char *git_pathdup_submodule(const char *path, const char *fmt, ...)
-       __attribute__((format (printf, 2, 3)));
 
+
+/*
+ * The `git_path` family of functions will construct a path into a repository's
+ * git directory.
+ *
+ * These functions will perform adjustments to the resultant path to account
+ * for special paths which are either considered common among worktrees (e.g.
+ * paths into the object directory) or have been explicitly set via an
+ * environment variable or config (e.g. path to the index file).
+ *
+ * For an exhaustive list of the adjustments made look at `common_list` and
+ * `adjust_git_path` in path.c.
+ */
+
+/*
+ * Return a path into the git directory of repository `repo`.
+ */
 extern char *repo_git_path(const struct repository *repo,
                           const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
+
+/*
+ * Construct a path into the git directory of repository `repo` and append it
+ * to the provided buffer `sb`.
+ */
 extern void strbuf_repo_git_path(struct strbuf *sb,
                                 const struct repository *repo,
                                 const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
 
+/*
+ * Return a statically allocated path into the main repository's
+ * (the_repository) git directory.
+ */
+extern const char *git_path(const char *fmt, ...)
+       __attribute__((format (printf, 1, 2)));
+
+/*
+ * Return a path into the main repository's (the_repository) git directory.
+ */
+extern char *git_pathdup(const char *fmt, ...)
+       __attribute__((format (printf, 1, 2)));
+
+/*
+ * Construct a path into the main repository's (the_repository) git directory
+ * and place it in the provided buffer `buf`, the contents of the buffer will
+ * be overridden.
+ */
+extern char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
+       __attribute__((format (printf, 2, 3)));
+
+/*
+ * Construct a path into the main repository's (the_repository) git directory
+ * and append it to the provided buffer `sb`.
+ */
+extern void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
+       __attribute__((format (printf, 2, 3)));
+
+/*
+ * Return a path into the worktree of repository `repo`.
+ *
+ * If the repository doesn't have a worktree NULL is returned.
+ */
 extern char *repo_worktree_path(const struct repository *repo,
                                const char *fmt, ...)
        __attribute__((format (printf, 2, 3)));
+
+/*
+ * Construct a path into the worktree of repository `repo` and append it
+ * to the provided buffer `sb`.
+ *
+ * If the repository doesn't have a worktree nothing will be appended to `sb`.
+ */
 extern void strbuf_repo_worktree_path(struct strbuf *sb,
                                      const struct repository *repo,
                                      const char *fmt, ...)
        __attribute__((format (printf, 3, 4)));
 
+/*
+ * Return a path into a submodule's git directory located at `path`.  `path`
+ * must only reference a submodule of the main repository (the_repository).
+ */
+extern char *git_pathdup_submodule(const char *path, const char *fmt, ...)
+       __attribute__((format (printf, 2, 3)));
+
+/*
+ * Construct a path into a submodule's git directory located at `path` and
+ * append it to the provided buffer `sb`.  `path` must only reference a
+ * submodule of the main repository (the_repository).
+ */
+extern int strbuf_git_path_submodule(struct strbuf *sb, const char *path,
+                                    const char *fmt, ...)
+       __attribute__((format (printf, 3, 4)));
+
 extern void report_linked_checkout_garbage(void);
 
 /*
index f6a3da5cd969765ada5ef536f08fb0cf4846cd39..72f2b4572ee798f9e17d6f4fbc0d12cfcd2fe221 100644 (file)
@@ -1855,6 +1855,8 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
                revs->dense = 0;
        } else if (!strcmp(arg, "--show-all")) {
                revs->show_all = 1;
+       } else if (!strcmp(arg, "--in-commit-order")) {
+               revs->tree_blobs_in_commit_order = 1;
        } else if (!strcmp(arg, "--remove-empty")) {
                revs->remove_empty_trees = 1;
        } else if (!strcmp(arg, "--merges")) {
index 747bce8d8a184003b406d0d3b93d75371c1af5fe..19dc9bdddba20dfa6ef1bdaaee0e82db3bdf8bd8 100644 (file)
@@ -121,7 +121,8 @@ struct rev_info {
                        bisect:1,
                        ancestry_path:1,
                        first_parent_only:1,
-                       line_level_traverse:1;
+                       line_level_traverse:1,
+                       tree_blobs_in_commit_order:1;
 
        /* Diff flags */
        unsigned int    diff:1,
index a8cc6b266e67847abafa613184752b3b781eb8dd..2112d3b27ad21e1d457b08a3aa71a811b5e8ee79 100644 (file)
@@ -58,35 +58,25 @@ static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struc
         * the revision parameters to it via its stdin and
         * let its stdout go back to the other end.
         */
-       const char *argv[] = {
-               "pack-objects",
-               "--all-progress-implied",
-               "--revs",
-               "--stdout",
-               NULL,
-               NULL,
-               NULL,
-               NULL,
-               NULL,
-               NULL,
-       };
        struct child_process po = CHILD_PROCESS_INIT;
        FILE *po_in;
        int i;
        int rc;
 
-       i = 4;
+       argv_array_push(&po.args, "pack-objects");
+       argv_array_push(&po.args, "--all-progress-implied");
+       argv_array_push(&po.args, "--revs");
+       argv_array_push(&po.args, "--stdout");
        if (args->use_thin_pack)
-               argv[i++] = "--thin";
+               argv_array_push(&po.args, "--thin");
        if (args->use_ofs_delta)
-               argv[i++] = "--delta-base-offset";
+               argv_array_push(&po.args, "--delta-base-offset");
        if (args->quiet || !args->progress)
-               argv[i++] = "-q";
+               argv_array_push(&po.args, "-q");
        if (args->progress)
-               argv[i++] = "--progress";
+               argv_array_push(&po.args, "--progress");
        if (is_repository_shallow())
-               argv[i++] = "--shallow";
-       po.argv = argv;
+               argv_array_push(&po.args, "--shallow");
        po.in = -1;
        po.out = args->stateless_rpc ? -1 : fd;
        po.git_cmd = 1;
index 894d12ad725c2f8cdbabbc87c7db558f5a872a4e..4d3f60594cbf0e9ddf2ea78e8c58eca312b4cac0 100644 (file)
@@ -493,7 +493,7 @@ static int is_index_unchanged(void)
        struct commit *head_commit;
 
        if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, &head_oid, NULL))
-               return error(_("could not resolve HEAD commit\n"));
+               return error(_("could not resolve HEAD commit"));
 
        head_commit = lookup_commit(&head_oid);
 
@@ -513,7 +513,7 @@ static int is_index_unchanged(void)
 
        if (!cache_tree_fully_valid(active_cache_tree))
                if (cache_tree_update(&the_index, 0))
-                       return error(_("unable to update cache tree\n"));
+                       return error(_("unable to update cache tree"));
 
        return !oidcmp(&active_cache_tree->oid,
                       &head_commit->tree->object.oid);
@@ -699,12 +699,12 @@ static int is_original_commit_empty(struct commit *commit)
        const struct object_id *ptree_oid;
 
        if (parse_commit(commit))
-               return error(_("could not parse commit %s\n"),
+               return error(_("could not parse commit %s"),
                             oid_to_hex(&commit->object.oid));
        if (commit->parents) {
                struct commit *parent = commit->parents->item;
                if (parse_commit(parent))
-                       return error(_("could not parse parent commit %s\n"),
+                       return error(_("could not parse parent commit %s"),
                                oid_to_hex(&parent->object.oid));
                ptree_oid = &parent->tree->object.oid;
        } else {
@@ -1013,9 +1013,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                        opts);
                if (res || command != TODO_REWORD)
                        goto leave;
-               flags |= EDIT_MSG | AMEND_MSG;
-               if (command == TODO_REWORD)
-                       flags |= VERIFY_MSG;
+               flags |= EDIT_MSG | AMEND_MSG | VERIFY_MSG;
                msg_file = NULL;
                goto fast_forward_edit;
        }
@@ -1263,18 +1261,23 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
        if (i >= TODO_COMMENT)
                return -1;
 
+       /* Eat up extra spaces/ tabs before object name */
+       padding = strspn(bol, " \t");
+       bol += padding;
+
        if (item->command == TODO_NOOP) {
+               if (bol != eol)
+                       return error(_("%s does not accept arguments: '%s'"),
+                                    command_to_string(item->command), bol);
                item->commit = NULL;
                item->arg = bol;
                item->arg_len = eol - bol;
                return 0;
        }
 
-       /* Eat up extra spaces/ tabs before object name */
-       padding = strspn(bol, " \t");
        if (!padding)
-               return -1;
-       bol += padding;
+               return error(_("missing arguments for %s"),
+                            command_to_string(item->command));
 
        if (item->command == TODO_EXEC) {
                item->commit = NULL;
@@ -2583,7 +2586,10 @@ int transform_todos(unsigned flags)
                        strbuf_addf(&buf, " %s", oid);
                }
                /* add all the rest */
-               strbuf_addf(&buf, " %.*s\n", item->arg_len, item->arg);
+               if (!item->arg_len)
+                       strbuf_addch(&buf, '\n');
+               else
+                       strbuf_addf(&buf, " %.*s\n", item->arg_len, item->arg);
        }
 
        i = write_message(buf.buf, buf.len, todo_file, 0);
index ac5a7ab62d554fb292d29df9cb618a336c5336c0..1df674e9194ee6d5cd5386f477745ff6639b7b65 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -11,6 +11,28 @@ int starts_with(const char *str, const char *prefix)
                        return 0;
 }
 
+int skip_to_optional_arg_default(const char *str, const char *prefix,
+                                const char **arg, const char *def)
+{
+       const char *p;
+
+       if (!skip_prefix(str, prefix, &p))
+               return 0;
+
+       if (!*p) {
+               if (arg)
+                       *arg = def;
+               return 1;
+       }
+
+       if (*p != '=')
+               return 0;
+
+       if (arg)
+               *arg = p + 1;
+       return 1;
+}
+
 /*
  * Used as the default ->buf value, so that people can always assume
  * buf is non NULL and ->buf is NUL terminated even for a freshly
@@ -661,7 +683,7 @@ static void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len,
                    (!reserved && is_rfc3986_reserved(ch)))
                        strbuf_addch(sb, ch);
                else
-                       strbuf_addf(sb, "%%%02x", ch);
+                       strbuf_addf(sb, "%%%02x", (unsigned char)ch);
        }
 }
 
index 1bb06c36f22d9fa0fb01fb8dfad02ffa9f338ebd..96317a35f4df9944cd3b9f64db0db3132d69da6e 100644 (file)
@@ -8,6 +8,7 @@
 
 #GIT_TEST_OPTS = --verbose --debug
 SHELL_PATH ?= $(SHELL)
+TEST_SHELL_PATH ?= $(SHELL_PATH)
 PERL_PATH ?= /usr/bin/perl
 TAR ?= $(TAR)
 RM ?= rm -f
@@ -23,6 +24,7 @@ endif
 
 # Shell quote;
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+TEST_SHELL_PATH_SQ = $(subst ','\'',$(TEST_SHELL_PATH))
 PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
 TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
 
@@ -42,11 +44,11 @@ failed:
        test -z "$$failed" || $(MAKE) $$failed
 
 prove: pre-clean $(TEST_LINT)
-       @echo "*** prove ***"; $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
+       @echo "*** prove ***"; $(PROVE) --exec '$(TEST_SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
        $(MAKE) clean-except-prove-cache
 
 $(T):
-       @echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+       @echo "*** $@ ***"; '$(TEST_SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
 pre-clean:
        $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
index 03dc9d28525730cdf57c74d13cab22da59dcdfbd..e07f0284372ec25c91fd62f0d136c31d80263a0d 100755 (executable)
@@ -21,6 +21,7 @@ sub err {
        /^\s*declare\s+/ and err 'arrays/declare not portable';
        /^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)';
        /\btest\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
+       /\bwc -l.*"\s*=/ and err '`"$(wc -l)"` is not portable (please use test_line_count)';
        /\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (please use FOO=bar && export FOO)';
        # this resets our $. for each file
        close ARGV if eof;
index 6368a89345e1783ec975c5f6c2c2c66b7ddb9dcb..297fb01d61eded5d83d02175199efcd217376738 100644 (file)
@@ -112,7 +112,7 @@ static void analyze_run(void)
 {
        uint64_t t1s, t1m, t2s, t2m;
        int cache_nr_limit;
-       int nr_threads_used;
+       int nr_threads_used = 0;
        int i;
        int nr;
 
index 688313ed5cc40e22b2412c57f98df06e4f3ac0ea..4c1f81f1678d83ab8b9ee7704d400bdd47ea70f7 100644 (file)
@@ -17,8 +17,8 @@ SVN_TREE=$GIT_SVN_DIR/svn-tree
 svn >/dev/null 2>&1
 if test $? -ne 1
 then
-    skip_all='skipping git svn tests, svn not found'
-    test_done
+       skip_all='skipping git svn tests, svn not found'
+       test_done
 fi
 
 svnrepo=$PWD/svnrepo
@@ -110,18 +110,20 @@ EOF
 }
 
 require_svnserve () {
-    if test -z "$SVNSERVE_PORT"
-    then
-       skip_all='skipping svnserve test. (set $SVNSERVE_PORT to enable)'
-        test_done
-    fi
+       test_tristate GIT_TEST_SVNSERVE
+       if ! test "$GIT_TEST_SVNSERVE" = true
+       then
+               skip_all='skipping svnserve test. (set $GIT_TEST_SVNSERVE to enable)'
+               test_done
+       fi
 }
 
 start_svnserve () {
-    svnserve --listen-port $SVNSERVE_PORT \
-             --root "$rawsvnrepo" \
-             --listen-once \
-             --listen-host 127.0.0.1 &
+       SVNSERVE_PORT=${SVNSERVE_PORT-${this_test#t}}
+       svnserve --listen-port $SVNSERVE_PORT \
+                --root "$rawsvnrepo" \
+                --listen-once \
+                --listen-host 127.0.0.1 &
 }
 
 prepare_a_utf8_locale () {
index 16d1bf72e5ee800a276c4d1ec474a9cbc75b385a..65e145c02d9eafc5a43d56c1fb30fef7be63d704 100755 (executable)
@@ -40,8 +40,7 @@ test_lazy_prereq UNTRACKED_CACHE '
 '
 
 test_lazy_prereq WATCHMAN '
-       { command -v watchman >/dev/null 2>&1; ret=$?; } &&
-       test $ret -ne 1
+       command -v watchman
 '
 
 if test_have_prereq WATCHMAN
index 3950f5034d319be99695c9332b22a8d26e20ff32..6471a68701dd4bc140d43ba91ebb899036ae0d4d 100755 (executable)
@@ -12,62 +12,76 @@ test_expect_success 'setup' '
        git commit -m one
 '
 
-check_diff() {
-expect=$1; shift
-cat >expected <<EOF
-diff --git a/$expect b/$expect
-new file mode 100644
-index 0000000..25c05ef
---- /dev/null
-+++ b/$expect
-@@ -0,0 +1 @@
-+other content
-EOF
-test_expect_success "-p $*" "
-       git diff -p $* HEAD^ >actual &&
-       test_cmp expected actual
-"
+check_diff () {
+       dir=$1
+       shift
+       expect=$1
+       shift
+       cat >expected <<-EOF
+       diff --git a/$expect b/$expect
+       new file mode 100644
+       index 0000000..25c05ef
+       --- /dev/null
+       +++ b/$expect
+       @@ -0,0 +1 @@
+       +other content
+       EOF
+       test_expect_success "-p $*" "
+               git -C '$dir' diff -p $* HEAD^ >actual &&
+               test_cmp expected actual
+       "
 }
 
-check_numstat() {
-expect=$1; shift
-cat >expected <<EOF
-1      0       $expect
-EOF
-test_expect_success "--numstat $*" "
-       echo '1 0       $expect' >expected &&
-       git diff --numstat $* HEAD^ >actual &&
-       test_cmp expected actual
-"
+check_numstat () {
+       dir=$1
+       shift
+       expect=$1
+       shift
+       cat >expected <<-EOF
+       1       0       $expect
+       EOF
+       test_expect_success "--numstat $*" "
+               echo '1 0       $expect' >expected &&
+               git -C '$dir' diff --numstat $* HEAD^ >actual &&
+               test_cmp expected actual
+       "
 }
 
-check_stat() {
-expect=$1; shift
-cat >expected <<EOF
- $expect | 1 +
- 1 file changed, 1 insertion(+)
-EOF
-test_expect_success "--stat $*" "
-       git diff --stat $* HEAD^ >actual &&
-       test_i18ncmp expected actual
-"
+check_stat () {
+       dir=$1
+       shift
+       expect=$1
+       shift
+       cat >expected <<-EOF
+        $expect | 1 +
+        1 file changed, 1 insertion(+)
+       EOF
+       test_expect_success "--stat $*" "
+               git -C '$dir' diff --stat $* HEAD^ >actual &&
+               test_i18ncmp expected actual
+       "
 }
 
-check_raw() {
-expect=$1; shift
-cat >expected <<EOF
-:000000 100644 0000000000000000000000000000000000000000 25c05ef3639d2d270e7fe765a67668f098092bc5 A     $expect
-EOF
-test_expect_success "--raw $*" "
-       git diff --no-abbrev --raw $* HEAD^ >actual &&
-       test_cmp expected actual
-"
+check_raw () {
+       dir=$1
+       shift
+       expect=$1
+       shift
+       cat >expected <<-EOF
+       :000000 100644 0000000000000000000000000000000000000000 25c05ef3639d2d270e7fe765a67668f098092bc5 A      $expect
+       EOF
+       test_expect_success "--raw $*" "
+               git -C '$dir' diff --no-abbrev --raw $* HEAD^ >actual &&
+               test_cmp expected actual
+       "
 }
 
-for type in diff numstat stat raw; do
-       check_$type file2 --relative=subdir/
-       check_$type file2 --relative=subdir
-       check_$type dir/file2 --relative=sub
+for type in diff numstat stat raw
+do
+       check_$type . file2 --relative=subdir/
+       check_$type . file2 --relative=subdir
+       check_$type subdir file2 --relative
+       check_$type . dir/file2 --relative=sub
 done
 
 test_done
diff --git a/t/t5573-pull-verify-signatures.sh b/t/t5573-pull-verify-signatures.sh
new file mode 100755 (executable)
index 0000000..9594e89
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+test_description='pull signature verification tests'
+. ./test-lib.sh
+. "$TEST_DIRECTORY/lib-gpg.sh"
+
+test_expect_success GPG 'create repositories with signed commits' '
+       echo 1 >a && git add a &&
+       test_tick && git commit -m initial &&
+       git tag initial &&
+
+       git clone . signed &&
+       (
+               cd signed &&
+               echo 2 >b && git add b &&
+               test_tick && git commit -S -m "signed"
+       ) &&
+
+       git clone . unsigned &&
+       (
+               cd unsigned &&
+               echo 3 >c && git add c &&
+               test_tick && git commit -m "unsigned"
+       ) &&
+
+       git clone . bad &&
+       (
+               cd bad &&
+               echo 4 >d && git add d &&
+               test_tick && git commit -S -m "bad" &&
+               git cat-file commit HEAD >raw &&
+               sed -e "s/bad/forged bad/" raw >forged &&
+               git hash-object -w -t commit forged >forged.commit &&
+               git checkout $(cat forged.commit)
+       ) &&
+
+       git clone . untrusted &&
+       (
+               cd untrusted &&
+               echo 5 >e && git add e &&
+               test_tick && git commit -SB7227189 -m "untrusted"
+       )
+'
+
+test_expect_success GPG 'pull unsigned commit with --verify-signatures' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_must_fail git pull --ff-only --verify-signatures unsigned 2>pullerror &&
+       test_i18ngrep "does not have a GPG signature" pullerror
+'
+
+test_expect_success GPG 'pull commit with bad signature with --verify-signatures' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_must_fail git pull --ff-only --verify-signatures bad 2>pullerror &&
+       test_i18ngrep "has a bad GPG signature" pullerror
+'
+
+test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
+       test_i18ngrep "has an untrusted GPG signature" pullerror
+'
+
+test_expect_success GPG 'pull signed commit with --verify-signatures' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       git pull --verify-signatures signed >pulloutput &&
+       test_i18ngrep "has a good GPG signature" pulloutput
+'
+
+test_expect_success GPG 'pull commit with bad signature without verification' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       git pull --ff-only bad 2>pullerror
+'
+
+test_expect_success GPG 'pull commit with bad signature with --no-verify-signatures' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config merge.verifySignatures true &&
+       test_config pull.verifySignatures true &&
+       git pull --ff-only --no-verify-signatures bad 2>pullerror
+'
+
+test_done
index d2d883f3a1d473a2b69fab05adbb21e67e5e600c..b4905b822c07048ddfbc43cfd39265bd2116f197 100755 (executable)
@@ -7,9 +7,9 @@ check_obj () {
        alt=$1; shift
        while read obj expect
        do
-               echo "$obj" >&3 &&
-               echo "$obj $expect" >&4
-       done 3>input 4>expect &&
+               echo "$obj" >&5 &&
+               echo "$obj $expect" >&6
+       done 5>input 6>expect &&
        GIT_ALTERNATE_OBJECT_DIRECTORIES=$alt \
                git "$@" cat-file --batch-check='%(objectname) %(objecttype)' \
                <input >actual &&
index 01023486c5363da00c9e381fcf1e7d5479c348ae..23b86fb9778980ab813d5e252065ebe34fe4736d 100755 (executable)
@@ -6,18 +6,21 @@ test_description="merges with unrelated index changes"
 
 # Testcase for some simple merges
 #   A
-#   o-----o B
+#   o-------o B
 #    \
-#     \---o C
+#     \-----o C
 #      \
-#       \-o D
+#       \---o D
 #        \
-#         o E
+#         \-o E
+#          \
+#           o F
 #   Commit A: some file a
 #   Commit B: adds file b, modifies end of a
 #   Commit C: adds file c
 #   Commit D: adds file d, modifies beginning of a
 #   Commit E: renames a->subdir/a, adds subdir/e
+#   Commit F: empty commit
 
 test_expect_success 'setup trivial merges' '
        test_seq 1 10 >a &&
@@ -29,6 +32,7 @@ test_expect_success 'setup trivial merges' '
        git branch C &&
        git branch D &&
        git branch E &&
+       git branch F &&
 
        git checkout B &&
        echo b >b &&
@@ -52,7 +56,10 @@ test_expect_success 'setup trivial merges' '
        git mv a subdir/a &&
        echo e >subdir/e &&
        git add subdir &&
-       test_tick && git commit -m E
+       test_tick && git commit -m E &&
+
+       git checkout F &&
+       test_tick && git commit --allow-empty -m F
 '
 
 test_expect_success 'ff update' '
@@ -105,6 +112,15 @@ test_expect_success 'recursive' '
        test_must_fail git merge -s recursive C^0
 '
 
+test_expect_success 'recursive, when merge branch matches merge base' '
+       git reset --hard &&
+       git checkout B^0 &&
+
+       touch random_file && git add random_file &&
+
+       test_must_fail git merge -s recursive F^0
+'
+
 test_expect_success 'octopus, unrelated file touched' '
        git reset --hard &&
        git checkout B^0 &&
diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh
new file mode 100755 (executable)
index 0000000..b2bb0a7
--- /dev/null
@@ -0,0 +1,77 @@
+#!/bin/sh
+
+test_description='rev-list testing in-commit-order'
+
+. ./test-lib.sh
+
+test_expect_success 'setup a commit history with trees, blobs' '
+       for x in one two three four
+       do
+               echo $x >$x &&
+               git add $x &&
+               git commit -m "add file $x" ||
+               return 1
+       done &&
+       for x in four three
+       do
+               git rm $x &&
+               git commit -m "remove $x" ||
+               return 1
+       done
+'
+
+test_expect_success 'rev-list --in-commit-order' '
+       git rev-list --in-commit-order --objects HEAD >actual.raw &&
+       cut -c 1-40 >actual <actual.raw &&
+
+       git cat-file --batch-check="%(objectname)" >expect.raw <<-\EOF &&
+               HEAD^{commit}
+               HEAD^{tree}
+               HEAD^{tree}:one
+               HEAD^{tree}:two
+               HEAD~1^{commit}
+               HEAD~1^{tree}
+               HEAD~1^{tree}:three
+               HEAD~2^{commit}
+               HEAD~2^{tree}
+               HEAD~2^{tree}:four
+               HEAD~3^{commit}
+               # HEAD~3^{tree} skipped, same as HEAD~1^{tree}
+               HEAD~4^{commit}
+               # HEAD~4^{tree} skipped, same as HEAD^{tree}
+               HEAD~5^{commit}
+               HEAD~5^{tree}
+       EOF
+       grep -v "#" >expect <expect.raw &&
+
+       test_cmp expect actual
+'
+
+test_expect_success 'rev-list lists blobs and trees after commits' '
+       git rev-list --objects HEAD >actual.raw &&
+       cut -c 1-40 >actual <actual.raw &&
+
+       git cat-file --batch-check="%(objectname)" >expect.raw <<-\EOF &&
+               HEAD^{commit}
+               HEAD~1^{commit}
+               HEAD~2^{commit}
+               HEAD~3^{commit}
+               HEAD~4^{commit}
+               HEAD~5^{commit}
+               HEAD^{tree}
+               HEAD^{tree}:one
+               HEAD^{tree}:two
+               HEAD~1^{tree}
+               HEAD~1^{tree}:three
+               HEAD~2^{tree}
+               HEAD~2^{tree}:four
+               # HEAD~3^{tree} skipped, same as HEAD~1^{tree}
+               # HEAD~4^{tree} skipped, same as HEAD^{tree}
+               HEAD~5^{tree}
+       EOF
+       grep -v "#" >expect <expect.raw &&
+
+       test_cmp expect actual
+'
+
+test_done
index 1c0e8659d97c0792ce5761cf0704aa7f061575e0..3e3fb462a098c01c72270acbb276abd2df225a66 100755 (executable)
@@ -304,12 +304,46 @@ test_expect_success 'describe chokes on severely broken submodules' '
        mv .git/modules/sub1/ .git/modules/sub_moved &&
        test_must_fail git describe --dirty
 '
-test_expect_success 'describe ignoring a borken submodule' '
+test_expect_success 'describe ignoring a broken submodule' '
        git describe --broken >out &&
        test_when_finished "mv .git/modules/sub_moved .git/modules/sub1" &&
        grep broken out
 '
 
+test_expect_success 'describe a blob at a directly tagged commit' '
+       echo "make it a unique blob" >file &&
+       git add file && git commit -m "content in file" &&
+       git tag -a -m "latest annotated tag" unique-file &&
+       git describe HEAD:file >actual &&
+       echo "unique-file:file" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'describe a blob with its first introduction' '
+       git commit --allow-empty -m "empty commit" &&
+       git rm file &&
+       git commit -m "delete blob" &&
+       git revert HEAD &&
+       git commit --allow-empty -m "empty commit" &&
+       git describe HEAD:file >actual &&
+       echo "unique-file:file" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'describe directly tagged blob' '
+       git tag test-blob unique-file:file &&
+       git describe test-blob >actual &&
+       echo "unique-file:file" >expect &&
+       # suboptimal: we rather want to see "test-blob"
+       test_cmp expect actual
+'
+
+test_expect_success 'describe tag object' '
+       git tag test-blob-1 -a -m msg unique-file:file &&
+       test_must_fail git describe test-blob-1 2>actual &&
+       test_i18ngrep "fatal: test-blob-1 is neither a commit nor blob" actual
+'
+
 test_expect_failure ULIMIT_STACK_SIZE 'name-rev works in a deep repo' '
        i=1 &&
        while test $i -lt 8000
index 8ae69a61c33a2691a142759f45a8addd36322343..e797c741124ef86eae0e3a8d6a5372b0a84c91e2 100755 (executable)
@@ -35,27 +35,72 @@ test_expect_success GPG 'create signed commits' '
 '
 
 test_expect_success GPG 'merge unsigned commit with verification' '
+       test_when_finished "git reset --hard && git checkout initial" &&
        test_must_fail git merge --ff-only --verify-signatures side-unsigned 2>mergeerror &&
        test_i18ngrep "does not have a GPG signature" mergeerror
 '
 
+test_expect_success GPG 'merge unsigned commit with merge.verifySignatures=true' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config merge.verifySignatures true &&
+       test_must_fail git merge --ff-only side-unsigned 2>mergeerror &&
+       test_i18ngrep "does not have a GPG signature" mergeerror
+'
+
 test_expect_success GPG 'merge commit with bad signature with verification' '
+       test_when_finished "git reset --hard && git checkout initial" &&
        test_must_fail git merge --ff-only --verify-signatures $(cat forged.commit) 2>mergeerror &&
        test_i18ngrep "has a bad GPG signature" mergeerror
 '
 
+test_expect_success GPG 'merge commit with bad signature with merge.verifySignatures=true' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config merge.verifySignatures true &&
+       test_must_fail git merge --ff-only $(cat forged.commit) 2>mergeerror &&
+       test_i18ngrep "has a bad GPG signature" mergeerror
+'
+
 test_expect_success GPG 'merge commit with untrusted signature with verification' '
+       test_when_finished "git reset --hard && git checkout initial" &&
        test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror &&
        test_i18ngrep "has an untrusted GPG signature" mergeerror
 '
 
+test_expect_success GPG 'merge commit with untrusted signature with merge.verifySignatures=true' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config merge.verifySignatures true &&
+       test_must_fail git merge --ff-only side-untrusted 2>mergeerror &&
+       test_i18ngrep "has an untrusted GPG signature" mergeerror
+'
+
 test_expect_success GPG 'merge signed commit with verification' '
+       test_when_finished "git reset --hard && git checkout initial" &&
        git merge --verbose --ff-only --verify-signatures side-signed >mergeoutput &&
        test_i18ngrep "has a good GPG signature" mergeoutput
 '
 
+test_expect_success GPG 'merge signed commit with merge.verifySignatures=true' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config merge.verifySignatures true &&
+       git merge --verbose --ff-only side-signed >mergeoutput &&
+       test_i18ngrep "has a good GPG signature" mergeoutput
+'
+
 test_expect_success GPG 'merge commit with bad signature without verification' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       git merge $(cat forged.commit)
+'
+
+test_expect_success GPG 'merge commit with bad signature with merge.verifySignatures=false' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config merge.verifySignatures false &&
        git merge $(cat forged.commit)
 '
 
+test_expect_success GPG 'merge commit with bad signature with merge.verifySignatures=true and --no-verify-signatures' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config merge.verifySignatures true &&
+       git merge --no-verify-signatures $(cat forged.commit)
+'
+
 test_done
diff --git a/t/t9169-git-svn-dcommit-crlf.sh b/t/t9169-git-svn-dcommit-crlf.sh
new file mode 100755 (executable)
index 0000000..54b1f61
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='git svn dcommit CRLF'
+. ./lib-git-svn.sh
+
+test_expect_success 'setup commit repository' '
+       svn_cmd mkdir -m "$test_description" "$svnrepo/dir" &&
+       git svn clone "$svnrepo" work &&
+       (
+               cd work &&
+               echo foo >>foo &&
+               git update-index --add foo &&
+               printf "a\\r\\n\\r\\nb\\r\\nc\\r\\n" >cmt &&
+               p=$(git rev-parse HEAD) &&
+               t=$(git write-tree) &&
+               cmt=$(git commit-tree -p $p $t <cmt) &&
+               git update-ref refs/heads/master $cmt &&
+               git cat-file commit HEAD | tail -n4 >out &&
+               test_cmp cmt out &&
+               git svn dcommit &&
+               printf "a\\n\\nb\\nc\\n" >exp &&
+               git cat-file commit HEAD | sed -ne 6,9p >out &&
+               test_cmp exp out
+       )
+'
+
+test_done
index 3457d5db64afc09f63e8c7de4c51b932b012c7ee..71cae2874d935e16b951160c9a3fa0014fe2db27 100755 (executable)
@@ -460,7 +460,13 @@ test_expect_success 'submit --shelve' '
        )
 '
 
-# Update an existing shelved changelist
+make_shelved_cl() {
+       test_commit "$1" >/dev/null &&
+       git p4 submit --origin HEAD^ --shelve >/dev/null &&
+       p4 -G changes -s shelved -m 1 | marshal_dump change
+}
+
+# Update existing shelved changelists
 
 test_expect_success 'submit --update-shelve' '
        test_when_finished cleanup_git &&
@@ -470,21 +476,19 @@ test_expect_success 'submit --update-shelve' '
                p4 revert ... &&
                cd "$git" &&
                git config git-p4.skipSubmitEdit true &&
-               test_commit "test-update-shelved-change" &&
-               git p4 submit --origin=HEAD^ --shelve &&
+               shelved_cl0=$(make_shelved_cl "shelved-change-0") &&
+               echo shelved_cl0=$shelved_cl0 &&
+               shelved_cl1=$(make_shelved_cl "shelved-change-1") &&
 
-               shelf_cl=$(p4 -G changes -s shelved -m 1 |\
-                       marshal_dump change) &&
-               test -n $shelf_cl &&
-               echo "updating shelved change list $shelf_cl" &&
+               echo "updating shelved change lists $shelved_cl0 and $shelved_cl1" &&
 
                echo "updated-line" >>shelf.t &&
                echo added-file.t >added-file.t &&
                git add shelf.t added-file.t &&
-               git rm -f test-update-shelved-change.t &&
+               git rm -f shelved-change-1.t &&
                git commit --amend -C HEAD &&
                git show --stat HEAD &&
-               git p4 submit -v --origin HEAD^ --update-shelve $shelf_cl &&
+               git p4 submit -v --origin HEAD~2 --update-shelve $shelved_cl0 --update-shelve $shelved_cl1 &&
                echo "done git p4 submit"
        ) &&
        (
@@ -494,7 +498,7 @@ test_expect_success 'submit --update-shelve' '
                p4 unshelve -c $change -s $change &&
                grep -q updated-line shelf.t &&
                p4 describe -S $change | grep added-file.t &&
-               test_path_is_missing test-update-shelved-change.t
+               test_path_is_missing shelved-change-1.t
        )
 '
 
index e7065df2bb028aff76f3169d84873a1a3d789571..9a0a21f49ae7bef3d0a1b653cce811d12499672e 100644 (file)
@@ -80,7 +80,7 @@ done,*)
        # from any previous runs.
        >"$GIT_TEST_TEE_OUTPUT_FILE"
 
-       (GIT_TEST_TEE_STARTED=done ${SHELL_PATH} "$0" "$@" 2>&1;
+       (GIT_TEST_TEE_STARTED=done ${TEST_SHELL_PATH} "$0" "$@" 2>&1;
         echo $? >"$BASE.exit") | tee -a "$GIT_TEST_TEE_OUTPUT_FILE"
        test "$(cat "$BASE.exit")" = 0
        exit
@@ -264,7 +264,6 @@ do
                shift ;;
        -x)
                trace=t
-               verbose=t
                shift ;;
        --verbose-log)
                verbose_log=t
@@ -283,6 +282,11 @@ then
        test -z "$verbose_log" && verbose=t
 fi
 
+if test -n "$trace" && test -z "$verbose_log"
+then
+       verbose=t
+fi
+
 if test -n "$color"
 then
        # Save the color control sequences now rather than run tput
@@ -586,7 +590,9 @@ maybe_setup_valgrind () {
 }
 
 want_trace () {
-       test "$trace" = t && test "$verbose" = t
+       test "$trace" = t && {
+               test "$verbose" = t || test "$verbose_log" = t
+       }
 }
 
 # This is a separate function because some tests use
@@ -601,26 +607,40 @@ test_eval_inner_ () {
 }
 
 test_eval_ () {
-       # We run this block with stderr redirected to avoid extra cruft
-       # during a "-x" trace. Once in "set -x" mode, we cannot prevent
+       # If "-x" tracing is in effect, then we want to avoid polluting stderr
+       # with non-test commands. But once in "set -x" mode, we cannot prevent
        # the shell from printing the "set +x" to turn it off (nor the saving
        # of $? before that). But we can make sure that the output goes to
        # /dev/null.
        #
-       # The test itself is run with stderr put back to &4 (so either to
-       # /dev/null, or to the original stderr if --verbose was used).
+       # There are a few subtleties here:
+       #
+       #   - we have to redirect descriptor 4 in addition to 2, to cover
+       #     BASH_XTRACEFD
+       #
+       #   - the actual eval has to come before the redirection block (since
+       #     it needs to see descriptor 4 to set up its stderr)
+       #
+       #   - likewise, any error message we print must be outside the block to
+       #     access descriptor 4
+       #
+       #   - checking $? has to come immediately after the eval, but it must
+       #     be _inside_ the block to avoid polluting the "set -x" output
+       #
+
+       test_eval_inner_ "$@" </dev/null >&3 2>&4
        {
-               test_eval_inner_ "$@" </dev/null >&3 2>&4
                test_eval_ret_=$?
                if want_trace
                then
                        set +x
-                       if test "$test_eval_ret_" != 0
-                       then
-                               say_color error >&4 "error: last command exited with \$?=$test_eval_ret_"
-                       fi
                fi
-       } 2>/dev/null
+       } 2>/dev/null 4>&2
+
+       if test "$test_eval_ret_" != 0 && want_trace
+       then
+               say_color error >&4 "error: last command exited with \$?=$test_eval_ret_"
+       fi
        return $test_eval_ret_
 }
 
index 413cd7b5518c930eda526f37f0797ffbf31e33c8..508015023176290ec0f8d78043d852cdd37d5ce6 100644 (file)
@@ -11,6 +11,7 @@
 #include "sigchain.h"
 #include "argv-array.h"
 #include "refs.h"
+#include "transport-internal.h"
 
 static int debug;
 
@@ -650,7 +651,7 @@ static int fetch(struct transport *transport,
 
        if (process_connect(transport, 0)) {
                do_take_over(transport);
-               return transport->fetch(transport, nr_heads, to_fetch);
+               return transport->vtable->fetch(transport, nr_heads, to_fetch);
        }
 
        count = 0;
@@ -990,7 +991,7 @@ static int push_refs(struct transport *transport,
 
        if (process_connect(transport, 1)) {
                do_take_over(transport);
-               return transport->push_refs(transport, remote_refs, flags);
+               return transport->vtable->push_refs(transport, remote_refs, flags);
        }
 
        if (!remote_refs) {
@@ -1038,7 +1039,7 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
 
        if (process_connect(transport, for_push)) {
                do_take_over(transport);
-               return transport->get_refs_list(transport, for_push);
+               return transport->vtable->get_refs_list(transport, for_push);
        }
 
        if (data->push && for_push)
@@ -1086,6 +1087,15 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
        return ret;
 }
 
+static struct transport_vtable vtable = {
+       set_helper_option,
+       get_refs_list,
+       fetch,
+       push_refs,
+       connect_helper,
+       release_helper
+};
+
 int transport_helper_init(struct transport *transport, const char *name)
 {
        struct helper_data *data = xcalloc(1, sizeof(*data));
@@ -1097,12 +1107,7 @@ int transport_helper_init(struct transport *transport, const char *name)
                debug = 1;
 
        transport->data = data;
-       transport->set_option = set_helper_option;
-       transport->get_refs_list = get_refs_list;
-       transport->fetch = fetch;
-       transport->push_refs = push_refs;
-       transport->disconnect = release_helper;
-       transport->connect = connect_helper;
+       transport->vtable = &vtable;
        transport->smart_options = &(data->transport_options);
        return 0;
 }
diff --git a/transport-internal.h b/transport-internal.h
new file mode 100644 (file)
index 0000000..3c1a29d
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef TRANSPORT_INTERNAL_H
+#define TRANSPORT_INTERNAL_H
+
+struct ref;
+struct transport;
+
+struct transport_vtable {
+       /**
+        * Returns 0 if successful, positive if the option is not
+        * recognized or is inapplicable, and negative if the option
+        * is applicable but the value is invalid.
+        **/
+       int (*set_option)(struct transport *connection, const char *name,
+                         const char *value);
+       /**
+        * Returns a list of the remote side's refs. In order to allow
+        * the transport to try to share connections, for_push is a
+        * hint as to whether the ultimate operation is a push or a fetch.
+        *
+        * If the transport is able to determine the remote hash for
+        * the ref without a huge amount of effort, it should store it
+        * in the ref's old_sha1 field; otherwise it should be all 0.
+        **/
+       struct ref *(*get_refs_list)(struct transport *transport, int for_push);
+
+       /**
+        * Fetch the objects for the given refs. Note that this gets
+        * an array, and should ignore the list structure.
+        *
+        * If the transport did not get hashes for refs in
+        * get_refs_list(), it should set the old_sha1 fields in the
+        * provided refs now.
+        **/
+       int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);
+
+       /**
+        * Push the objects and refs. Send the necessary objects, and
+        * then, for any refs where peer_ref is set and
+        * peer_ref->new_oid is different from old_oid, tell the
+        * remote side to update each ref in the list from old_oid to
+        * peer_ref->new_oid.
+        *
+        * Where possible, set the status for each ref appropriately.
+        *
+        * The transport must modify new_sha1 in the ref to the new
+        * value if the remote accepted the change. Note that this
+        * could be a different value from peer_ref->new_oid if the
+        * process involved generating new commits.
+        **/
+       int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
+       int (*connect)(struct transport *connection, const char *name,
+                      const char *executable, int fd[2]);
+
+       /** get_refs_list(), fetch(), and push_refs() can keep
+        * resources (such as a connection) reserved for further
+        * use. disconnect() releases these resources.
+        **/
+       int (*disconnect)(struct transport *connection);
+};
+
+#endif
index 7cc39b7c0b9c547752924c82038ed0ac226a22bf..fc802260f61c7496f4250b15ef6f5ab8dd316898 100644 (file)
@@ -17,6 +17,7 @@
 #include "string-list.h"
 #include "sha1-array.h"
 #include "sigchain.h"
+#include "transport-internal.h"
 
 static void set_upstreams(struct transport *transport, struct ref *refs,
        int pretend)
@@ -607,6 +608,15 @@ static int disconnect_git(struct transport *transport)
        return 0;
 }
 
+static struct transport_vtable taken_over_vtable = {
+       NULL,
+       get_refs_via_connect,
+       fetch_refs_via_pack,
+       git_transport_push,
+       NULL,
+       disconnect_git
+};
+
 void transport_take_over(struct transport *transport,
                         struct child_process *child)
 {
@@ -624,11 +634,7 @@ void transport_take_over(struct transport *transport,
        data->got_remote_heads = 0;
        transport->data = data;
 
-       transport->set_option = NULL;
-       transport->get_refs_list = get_refs_via_connect;
-       transport->fetch = fetch_refs_via_pack;
-       transport->push_refs = git_transport_push;
-       transport->disconnect = disconnect_git;
+       transport->vtable = &taken_over_vtable;
        transport->smart_options = &(data->options);
 
        transport->cannot_reuse = 1;
@@ -751,6 +757,24 @@ void transport_check_allowed(const char *type)
                die("transport '%s' not allowed", type);
 }
 
+static struct transport_vtable bundle_vtable = {
+       NULL,
+       get_refs_from_bundle,
+       fetch_refs_from_bundle,
+       NULL,
+       NULL,
+       close_bundle
+};
+
+static struct transport_vtable builtin_smart_vtable = {
+       NULL,
+       get_refs_via_connect,
+       fetch_refs_via_pack,
+       git_transport_push,
+       connect_git,
+       disconnect_git
+};
+
 struct transport *transport_get(struct remote *remote, const char *url)
 {
        const char *helper;
@@ -787,9 +811,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
                struct bundle_transport_data *data = xcalloc(1, sizeof(*data));
                transport_check_allowed("file");
                ret->data = data;
-               ret->get_refs_list = get_refs_from_bundle;
-               ret->fetch = fetch_refs_from_bundle;
-               ret->disconnect = close_bundle;
+               ret->vtable = &bundle_vtable;
                ret->smart_options = NULL;
        } else if (!is_url(url)
                || starts_with(url, "file://")
@@ -804,12 +826,7 @@ struct transport *transport_get(struct remote *remote, const char *url)
                 */
                struct git_transport_data *data = xcalloc(1, sizeof(*data));
                ret->data = data;
-               ret->set_option = NULL;
-               ret->get_refs_list = get_refs_via_connect;
-               ret->fetch = fetch_refs_via_pack;
-               ret->push_refs = git_transport_push;
-               ret->connect = connect_git;
-               ret->disconnect = disconnect_git;
+               ret->vtable = &builtin_smart_vtable;
                ret->smart_options = &(data->options);
 
                data->conn = NULL;
@@ -843,9 +860,9 @@ int transport_set_option(struct transport *transport,
                git_reports = set_git_option(transport->smart_options,
                                             name, value);
 
-       if (transport->set_option)
-               protocol_reports = transport->set_option(transport, name,
-                                                       value);
+       if (transport->vtable->set_option)
+               protocol_reports = transport->vtable->set_option(transport,
+                                                                name, value);
 
        /* If either report is 0, report 0 (success). */
        if (!git_reports || !protocol_reports)
@@ -968,7 +985,7 @@ int transport_push(struct transport *transport,
        *reject_reasons = 0;
        transport_verify_remote_names(refspec_nr, refspec);
 
-       if (transport->push_refs) {
+       if (transport->vtable->push_refs) {
                struct ref *remote_refs;
                struct ref *local_refs = get_local_heads();
                int match_flags = MATCH_REFS_NONE;
@@ -981,7 +998,7 @@ int transport_push(struct transport *transport,
                if (check_push_refs(local_refs, refspec_nr, refspec) < 0)
                        return -1;
 
-               remote_refs = transport->get_refs_list(transport, 1);
+               remote_refs = transport->vtable->get_refs_list(transport, 1);
 
                if (flags & TRANSPORT_PUSH_ALL)
                        match_flags |= MATCH_REFS_ALL;
@@ -1056,7 +1073,7 @@ int transport_push(struct transport *transport,
                }
 
                if (!(flags & TRANSPORT_RECURSE_SUBMODULES_ONLY))
-                       push_ret = transport->push_refs(transport, remote_refs, flags);
+                       push_ret = transport->vtable->push_refs(transport, remote_refs, flags);
                else
                        push_ret = 0;
                err = push_had_errors(remote_refs);
@@ -1090,7 +1107,7 @@ int transport_push(struct transport *transport,
 const struct ref *transport_get_remote_refs(struct transport *transport)
 {
        if (!transport->got_remote_refs) {
-               transport->remote_refs = transport->get_refs_list(transport, 0);
+               transport->remote_refs = transport->vtable->get_refs_list(transport, 0);
                transport->got_remote_refs = 1;
        }
 
@@ -1127,7 +1144,7 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
                        heads[nr_heads++] = rm;
        }
 
-       rc = transport->fetch(transport, nr_heads, heads);
+       rc = transport->vtable->fetch(transport, nr_heads, heads);
 
        free(heads);
        return rc;
@@ -1144,8 +1161,8 @@ void transport_unlock_pack(struct transport *transport)
 int transport_connect(struct transport *transport, const char *name,
                      const char *exec, int fd[2])
 {
-       if (transport->connect)
-               return transport->connect(transport, name, exec, fd);
+       if (transport->vtable->connect)
+               return transport->vtable->connect(transport, name, exec, fd);
        else
                die("Operation not supported by protocol");
 }
@@ -1153,8 +1170,8 @@ int transport_connect(struct transport *transport, const char *name,
 int transport_disconnect(struct transport *transport)
 {
        int ret = 0;
-       if (transport->disconnect)
-               ret = transport->disconnect(transport);
+       if (transport->vtable->disconnect)
+               ret = transport->vtable->disconnect(transport);
        free(transport);
        return ret;
 }
index ab4fe7f277480853a66867a11ee9432588de30ac..731c78b6795740f8e9bfb80741b0170671b37dd3 100644 (file)
@@ -30,6 +30,8 @@ enum transport_family {
 };
 
 struct transport {
+       const struct transport_vtable *vtable;
+
        struct remote *remote;
        const char *url;
        void *data;
@@ -59,58 +61,6 @@ struct transport {
         */
        const struct string_list *push_options;
 
-       /**
-        * Returns 0 if successful, positive if the option is not
-        * recognized or is inapplicable, and negative if the option
-        * is applicable but the value is invalid.
-        **/
-       int (*set_option)(struct transport *connection, const char *name,
-                         const char *value);
-
-       /**
-        * Returns a list of the remote side's refs. In order to allow
-        * the transport to try to share connections, for_push is a
-        * hint as to whether the ultimate operation is a push or a fetch.
-        *
-        * If the transport is able to determine the remote hash for
-        * the ref without a huge amount of effort, it should store it
-        * in the ref's old_sha1 field; otherwise it should be all 0.
-        **/
-       struct ref *(*get_refs_list)(struct transport *transport, int for_push);
-
-       /**
-        * Fetch the objects for the given refs. Note that this gets
-        * an array, and should ignore the list structure.
-        *
-        * If the transport did not get hashes for refs in
-        * get_refs_list(), it should set the old_sha1 fields in the
-        * provided refs now.
-        **/
-       int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);
-
-       /**
-        * Push the objects and refs. Send the necessary objects, and
-        * then, for any refs where peer_ref is set and
-        * peer_ref->new_oid is different from old_oid, tell the
-        * remote side to update each ref in the list from old_oid to
-        * peer_ref->new_oid.
-        *
-        * Where possible, set the status for each ref appropriately.
-        *
-        * The transport must modify new_sha1 in the ref to the new
-        * value if the remote accepted the change. Note that this
-        * could be a different value from peer_ref->new_oid if the
-        * process involved generating new commits.
-        **/
-       int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
-       int (*connect)(struct transport *connection, const char *name,
-                      const char *executable, int fd[2]);
-
-       /** get_refs_list(), fetch(), and push_refs() can keep
-        * resources (such as a connection) reserved for further
-        * use. disconnect() releases these resources.
-        **/
-       int (*disconnect)(struct transport *connection);
        char *pack_lockfile;
        signed verbose : 3;
        /**
index 6106a8098c8a46575f9afae5da9909b5c196f754..41b718c29e1b9fc2981d7d14a3d25e69c31a3030 100644 (file)
--- a/version.c
+++ b/version.c
@@ -3,6 +3,7 @@
 #include "strbuf.h"
 
 const char git_version_string[] = GIT_VERSION;
+const char git_built_from_commit_string[] = GIT_BUILT_FROM_COMMIT;
 
 const char *git_user_agent(void)
 {
index 6911a4f1558de0c0d0b880a5b859925869a3e86e..7c62e80577154d79bec050424945eb500d262a0f 100644 (file)
--- a/version.h
+++ b/version.h
@@ -2,6 +2,7 @@
 #define VERSION_H
 
 extern const char git_version_string[];
+extern const char git_built_from_commit_string[];
 
 const char *git_user_agent(void);
 const char *git_user_agent_sanitized(void);