Merge branch 'rs/more-uses-of-skip-prefix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 14 Oct 2014 17:50:07 +0000 (10:50 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 14 Oct 2014 17:50:07 +0000 (10:50 -0700)
* rs/more-uses-of-skip-prefix:
use skip_prefix() to avoid more magic numbers

88 files changed:
.gitignore
Documentation/RelNotes/2.2.0.txt
Documentation/config.txt
Documentation/git-push.txt
Documentation/git-rebase.txt
Documentation/git-receive-pack.txt
Documentation/git-tag.txt
Documentation/technical/api-lockfile.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/protocol-capabilities.txt
Makefile
archive.c
builtin/add.c
builtin/apply.c
builtin/checkout-index.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/describe.c
builtin/diff.c
builtin/gc.c
builtin/mailsplit.c
builtin/merge.c
builtin/mv.c
builtin/push.c
builtin/read-tree.c
builtin/receive-pack.c
builtin/reflog.c
builtin/reset.c
builtin/rm.c
builtin/send-pack.c
builtin/update-index.c
bulk-checkin.c
bulk-checkin.h
bundle.c
cache-tree.c
cache.h
commit.c
config.c
contrib/completion/git-completion.bash
convert.c
convert.h
copy.c
credential-store.c
daemon.c
fast-import.c
fetch-pack.c
gpg-interface.c
gpg-interface.h
http.c
lockfile.c
lockfile.h [new file with mode: 0644]
merge-recursive.c
merge.c
read-cache.c
refs.c
remote-curl.c
rerere.c
send-pack.c
send-pack.h
sequencer.c
sha1-lookup.c
sha1_file.c
shallow.c
sigchain.c
t/lib-credential.sh
t/lib-httpd/apache.conf
t/t0021-conversion.sh
t/t0064-sha1-array.sh [new file with mode: 0755]
t/t0090-cache-tree.sh
t/t1050-large.sh
t/t5000-tar-tree.sh
t/t5534-push-signed.sh [new file with mode: 0755]
t/t5541-http-push-smart.sh
t/t7004-tag.sh
t/test-lib.sh
tag.c
tag.h
test-regex.c
test-scrap-cache-tree.c
test-sha1-array.c [new file with mode: 0644]
test-sigchain.c
transport-helper.c
transport.c
transport.h
varint.c
varint.h
wrapper.c
index 5bfb234591a63446d9fccf7665e442f02d1c76b5..9ec40fa9fbc645532c9bc36b65521e6771d14324 100644 (file)
 /test-revision-walking
 /test-run-command
 /test-sha1
+/test-sha1-array
 /test-sigchain
 /test-string-list
 /test-subprocess
index 438f92e34427d9bd30676c4e45d975d0ee028b79..5f0c421365d52134030d14580258059b27a536b9 100644 (file)
@@ -12,6 +12,8 @@ Ports
 
 UI, Workflows & Features
 
+ * "git archive" learned to filter what gets archived with pathspec.
+
  * "git config --edit --global" starts from a skeletal per-user
    configuration file contents, instead of a total blank, when the
    user does not already have any.  This immediately reduces the
@@ -48,6 +50,14 @@ UI, Workflows & Features
    for a tagged commit, gained a cousin "%D" that just gives the
    "tagname" without frills.
 
+ * "git push" learned "--signed" push, that allows a push (i.e.
+   request to update the refs on the other side to point at a new
+   history, together with the transmission of necessary objects) to be
+   signed, so that it can be verified and audited, using the GPG
+   signature of the person who pushed, that the tips of branches at a
+   public repository really point the commits the pusher wanted to,
+   without having to "trust" the server.
+
 Performance, Internal Implementation, etc.
 
  * The API to manipulate the "refs" is currently undergoing a revamp
@@ -108,6 +118,9 @@ Performance, Internal Implementation, etc.
  * "git hash-object" was taught a "--literally" option to help
    debugging.
 
+ * When running a required clean filter, we do not have to mmap the
+   original before feeding the filter.  Instead, stream the file
+   contents directly to the filter and process its output.
 
 Also contains various documentation updates and code clean-ups.
 
index 3b5b24aeb7f16c03e06bd6f38fd89e9080011ce7..04a1e2f37e938f004c899d3fb6c59d282c2071ce 100644 (file)
@@ -2044,6 +2044,25 @@ receive.autogc::
        receiving data from git-push and updating refs.  You can stop
        it by setting this variable to false.
 
+receive.certnonceseed::
+       By setting this variable to a string, `git receive-pack`
+       will accept a `git push --signed` and verifies it by using
+       a "nonce" protected by HMAC using this string as a secret
+       key.
+
+receive.certnonceslop::
+       When a `git push --signed` sent a push certificate with a
+       "nonce" that was issued by a receive-pack serving the same
+       repository within this many seconds, export the "nonce"
+       found in the certificate to `GIT_PUSH_CERT_NONCE` to the
+       hooks (instead of what the receive-pack asked the sending
+       side to include).  This may allow writing checks in
+       `pre-receive` and `post-receive` a bit easier.  Instead of
+       checking `GIT_PUSH_CERT_NONCE_SLOP` environment variable
+       that records by how many seconds the nonce is stale to
+       decide if they want to accept the certificate, they only
+       can check `GIT_PUSH_CERT_NONCE_STATUS` is `OK`.
+
 receive.fsckObjects::
        If it is set to true, git-receive-pack will check all received
        objects. It will abort in the case of a malformed object or a
index c0d7403b9a144292f1bb8e4eb6b83da25086f0ee..b17283ab7a1cc73c5ec741e0da1128bea2a57a65 100644 (file)
@@ -10,7 +10,8 @@ SYNOPSIS
 --------
 [verse]
 'git push' [--all | --mirror | --tags] [--follow-tags] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
-          [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose] [-u | --set-upstream]
+          [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose]
+          [-u | --set-upstream] [--signed]
           [--force-with-lease[=<refname>[:<expect>]]]
           [--no-verify] [<repository> [<refspec>...]]
 
@@ -129,6 +130,12 @@ already exists on the remote side.
        from the remote but are pointing at commit-ish that are
        reachable from the refs being pushed.
 
+--signed::
+       GPG-sign the push request to update refs on the receiving
+       side, to allow it to be checked by the hooks and/or be
+       logged.  See linkgit:git-receive-pack[1] for the details
+       on the receiving end.
+
 --receive-pack=<git-receive-pack>::
 --exec=<git-receive-pack>::
        Path to the 'git-receive-pack' program on the remote
index 4138554912165478d182d5720d4d7b4c4ab78c05..924827dc2ec79fc4df83a572dcef89425b844f81 100644 (file)
@@ -21,15 +21,17 @@ If <branch> is specified, 'git rebase' will perform an automatic
 it remains on the current branch.
 
 If <upstream> is not specified, the upstream configured in
-branch.<name>.remote and branch.<name>.merge options will be usedsee
-linkgit:git-config[1] for details.  If you are currently not on any
-branch or if the current branch does not have a configured upstream,
-the rebase will abort.
+branch.<name>.remote and branch.<name>.merge options will be used (see
+linkgit:git-config[1] for details) and the `--fork-point` option is
+assumed.  If you are currently not on any branch or if the current
+branch does not have a configured upstream, the rebase will abort.
 
 All changes made by commits in the current branch but that are not
 in <upstream> are saved to a temporary area.  This is the same set
-of commits that would be shown by `git log <upstream>..HEAD` (or
-`git log HEAD`, if --root is specified).
+of commits that would be shown by `git log <upstream>..HEAD`; or by
+`git log 'fork_point'..HEAD`, if `--fork-point` is active (see the
+description on `--fork-point` below); or by `git log HEAD`, if the
+`--root` option is specified.
 
 The current branch is reset to <upstream>, or <newbase> if the
 --onto option was supplied.  This has the exact same effect as
@@ -327,13 +329,18 @@ link:howto/revert-a-faulty-merge.html[revert-a-faulty-merge How-To] for details)
 
 --fork-point::
 --no-fork-point::
-       Use 'git merge-base --fork-point' to find a better common ancestor
-       between `upstream` and `branch` when calculating which commits have
-       have been introduced by `branch` (see linkgit:git-merge-base[1]).
+       Use reflog to find a better common ancestor between <upstream>
+       and <branch> when calculating which commits have been
+       introduced by <branch>.
 +
-If no non-option arguments are given on the command line, then the default is
-`--fork-point @{u}` otherwise the `upstream` argument is interpreted literally
-unless the `--fork-point` option is specified.
+When --fork-point is active, 'fork_point' will be used instead of
+<upstream> to calculate the set of commits to rebase, where
+'fork_point' is the result of `git merge-base --fork-point <upstream>
+<branch>` command (see linkgit:git-merge-base[1]).  If 'fork_point'
+ends up being empty, the <upstream> will be used as a fallback.
++
+If either <upstream> or --root is given on the command line, then the
+default is `--no-fork-point`, otherwise the default is `--fork-point`.
 
 --ignore-whitespace::
 --whitespace=<option>::
index b1f7dc643a0e9b6b232e1c75b39d7f784ab21b4a..9016960e27164a11d687ed16b34245667e271fe3 100644 (file)
@@ -53,6 +53,56 @@ the update.  Refs to be created will have sha1-old equal to 0\{40},
 while refs to be deleted will have sha1-new equal to 0\{40}, otherwise
 sha1-old and sha1-new should be valid objects in the repository.
 
+When accepting a signed push (see linkgit:git-push[1]), the signed
+push certificate is stored in a blob and an environment variable
+`GIT_PUSH_CERT` can be consulted for its object name.  See the
+description of `post-receive` hook for an example.  In addition, the
+certificate is verified using GPG and the result is exported with
+the following environment variables:
+
+`GIT_PUSH_CERT_SIGNER`::
+       The name and the e-mail address of the owner of the key that
+       signed the push certificate.
+
+`GIT_PUSH_CERT_KEY`::
+       The GPG key ID of the key that signed the push certificate.
+
+`GIT_PUSH_CERT_STATUS`::
+       The status of GPG verification of the push certificate,
+       using the same mnemonic as used in `%G?` format of `git log`
+       family of commands (see linkgit:git-log[1]).
+
+`GIT_PUSH_CERT_NONCE`::
+       The nonce string the process asked the signer to include
+       in the push certificate.  If this does not match the value
+       recorded on the "nonce" header in the push certificate, it
+       may indicate that the certificate is a valid one that is
+       being replayed from a separate "git push" session.
+
+`GIT_PUSH_CERT_NONCE_STATUS`::
+`UNSOLICITED`;;
+       "git push --signed" sent a nonce when we did not ask it to
+       send one.
+`MISSING`;;
+       "git push --signed" did not send any nonce header.
+`BAD`;;
+       "git push --signed" sent a bogus nonce.
+`OK`;;
+       "git push --signed" sent the nonce we asked it to send.
+`SLOP`;;
+       "git push --signed" sent a nonce different from what we
+       asked it to send now, but in a previous session.  See
+       `GIT_PUSH_CERT_NONCE_SLOP` environment variable.
+
+`GIT_PUSH_CERT_NONCE_SLOP`::
+       "git push --signed" sent a nonce different from what we
+       asked it to send now, but in a different session whose
+       starting time is different by this many seconds from the
+       current session.  Only meaningful when
+       `GIT_PUSH_CERT_NONCE_STATUS` says `SLOP`.
+       Also read about `receive.certnonceslop` variable in
+       linkgit:git-config[1].
+
 This hook is called before any refname is updated and before any
 fast-forward checks are performed.
 
@@ -101,9 +151,14 @@ the update.  Refs that were created will have sha1-old equal to
 0\{40}, otherwise sha1-old and sha1-new should be valid objects in
 the repository.
 
+The `GIT_PUSH_CERT*` environment variables can be inspected, just as
+in `pre-receive` hook, after accepting a signed push.
+
 Using this hook, it is easy to generate mails describing the updates
 to the repository.  This example script sends one mail message per
-ref listing the commits pushed to the repository:
+ref listing the commits pushed to the repository, and logs the push
+certificates of signed pushes with good signatures to a logger
+service:
 
        #!/bin/sh
        # mail out commit update information.
@@ -119,6 +174,14 @@ ref listing the commits pushed to the repository:
                fi |
                mail -s "Changes to ref $ref" commit-list@mydomain
        done
+       # log signed push certificate, if any
+       if test -n "${GIT_PUSH_CERT-}" && test ${GIT_PUSH_CERT_STATUS} = G
+       then
+               (
+                       echo expected nonce is ${GIT_PUSH_NONCE}
+                       git cat-file blob ${GIT_PUSH_CERT}
+               ) | mail -s "push certificate from $GIT_PUSH_CERT_SIGNER" push-log@mydomain
+       fi
        exit 0
 
 The exit code from this hook invocation is ignored, however a
index 320908369f067f5d494c6bc0c3c9f28fa26859a6..e953ba4439f5995c797b4d7ad869847a5479d982 100644 (file)
@@ -42,7 +42,7 @@ committer identity for the current user is used to find the
 GnuPG key for signing.         The configuration variable `gpg.program`
 is used to specify custom GnuPG binary.
 
-Tag objects (created with `-a`, `s`, or `-u`) are called "annotated"
+Tag objects (created with `-a`, `-s`, or `-u`) are called "annotated"
 tags; they contain a creation date, the tagger name and e-mail, a
 tagging message, and an optional GnuPG signature. Whereas a
 "lightweight" tag is simply a name for an object (usually a commit
index dd894043ae8b04269b3aa2108f96cb935217181d..93b5f23e4c8a1bb45dc26d5f9cec402df7f57ee4 100644 (file)
@@ -3,20 +3,132 @@ lockfile API
 
 The lockfile API serves two purposes:
 
-* Mutual exclusion.  When we write out a new index file, first
-  we create a new file `$GIT_DIR/index.lock`, write the new
-  contents into it, and rename it to the final destination
-  `$GIT_DIR/index`.  We try to create the `$GIT_DIR/index.lock`
-  file with O_EXCL so that we can notice and fail when somebody
-  else is already trying to update the index file.
-
-* Automatic cruft removal.  After we create the "lock" file, we
-  may decide to `die()`, and we would want to make sure that we
-  remove the file that has not been committed to its final
-  destination.  This is done by remembering the lockfiles we
-  created in a linked list and cleaning them up from an
-  `atexit(3)` handler.  Outstanding lockfiles are also removed
-  when the program dies on a signal.
+* Mutual exclusion and atomic file updates. When we want to change a
+  file, we create a lockfile `<filename>.lock`, write the new file
+  contents into it, and then rename the lockfile to its final
+  destination `<filename>`. We create the `<filename>.lock` file with
+  `O_CREAT|O_EXCL` so that we can notice and fail if somebody else has
+  already locked the file, then atomically rename the lockfile to its
+  final destination to commit the changes and unlock the file.
+
+* Automatic cruft removal. If the program exits after we lock a file
+  but before the changes have been committed, we want to make sure
+  that we remove the lockfile. This is done by remembering the
+  lockfiles we have created in a linked list and setting up an
+  `atexit(3)` handler and a signal handler that clean up the
+  lockfiles. This mechanism ensures that outstanding lockfiles are
+  cleaned up if the program exits (including when `die()` is called)
+  or if the program dies on a signal.
+
+Please note that lockfiles only block other writers. Readers do not
+block, but they are guaranteed to see either the old contents of the
+file or the new contents of the file (assuming that the filesystem
+implements `rename(2)` atomically).
+
+
+Calling sequence
+----------------
+
+The caller:
+
+* Allocates a `struct lock_file` either as a static variable or on the
+  heap, initialized to zeros. Once you use the structure to call the
+  `hold_lock_file_*` family of functions, it belongs to the lockfile
+  subsystem and its storage must remain valid throughout the life of
+  the program (i.e. you cannot use an on-stack variable to hold this
+  structure).
+
+* Attempts to create a lockfile by passing that variable and the path
+  of the final destination (e.g. `$GIT_DIR/index`) to
+  `hold_lock_file_for_update` or `hold_lock_file_for_append`.
+
+* Writes new content for the destination file by either:
+
+  * writing to the file descriptor returned by the `hold_lock_file_*`
+    functions (also available via `lock->fd`).
+
+  * calling `fdopen_lock_file` to get a `FILE` pointer for the open
+    file and writing to the file using stdio.
+
+When finished writing, the caller can:
+
+* Close the file descriptor and rename the lockfile to its final
+  destination by calling `commit_lock_file` or `commit_lock_file_to`.
+
+* Close the file descriptor and remove the lockfile by calling
+  `rollback_lock_file`.
+
+* Close the file descriptor without removing or renaming the lockfile
+  by calling `close_lock_file`, and later call `commit_lock_file`,
+  `commit_lock_file_to`, `rollback_lock_file`, or `reopen_lock_file`.
+
+Even after the lockfile is committed or rolled back, the `lock_file`
+object must not be freed or altered by the caller. However, it may be
+reused; just pass it to another call of `hold_lock_file_for_update` or
+`hold_lock_file_for_append`.
+
+If the program exits before you have called one of `commit_lock_file`,
+`commit_lock_file_to`, `rollback_lock_file`, or `close_lock_file`, an
+`atexit(3)` handler will close and remove the lockfile, rolling back
+any uncommitted changes.
+
+If you need to close the file descriptor you obtained from a
+`hold_lock_file_*` function yourself, do so by calling
+`close_lock_file`. You should never call `close(2)` or `fclose(3)`
+yourself! Otherwise the `struct lock_file` structure would still think
+that the file descriptor needs to be closed, and a commit or rollback
+would result in duplicate calls to `close(2)`. Worse yet, if you close
+and then later open another file descriptor for a completely different
+purpose, then a commit or rollback might close that unrelated file
+descriptor.
+
+
+Error handling
+--------------
+
+The `hold_lock_file_*` functions return a file descriptor on success
+or -1 on failure (unless `LOCK_DIE_ON_ERROR` is used; see below). On
+errors, `errno` describes the reason for failure. Errors can be
+reported by passing `errno` to one of the following helper functions:
+
+unable_to_lock_message::
+
+       Append an appropriate error message to a `strbuf`.
+
+unable_to_lock_error::
+
+       Emit an appropriate error message using `error()`.
+
+unable_to_lock_die::
+
+       Emit an appropriate error message and `die()`.
+
+Similarly, `commit_lock_file`, `commit_lock_file_to`, and
+`close_lock_file` return 0 on success. On failure they set `errno`
+appropriately, do their best to roll back the lockfile, and return -1.
+
+
+Flags
+-----
+
+The following flags can be passed to `hold_lock_file_for_update` or
+`hold_lock_file_for_append`:
+
+LOCK_NO_DEREF::
+
+       Usually symbolic links in the destination path are resolved
+       and the lockfile is created by adding ".lock" to the resolved
+       path. If `LOCK_NO_DEREF` is set, then the lockfile is created
+       by adding ".lock" to the path argument itself. This option is
+       used, for example, when locking a symbolic reference, which
+       for backwards-compatibility reasons can be a symbolic link
+       containing the name of the referred-to-reference.
+
+LOCK_DIE_ON_ERROR::
+
+       If a lock is already taken for the file, `die()` with an error
+       message. If this option is not specified, trying to lock a
+       file that is already locked returns -1 to the caller.
 
 
 The functions
@@ -24,51 +136,85 @@ The functions
 
 hold_lock_file_for_update::
 
-       Take a pointer to `struct lock_file`, the filename of
-       the final destination (e.g. `$GIT_DIR/index`) and a flag
-       `die_on_error`.  Attempt to create a lockfile for the
-       destination and return the file descriptor for writing
-       to the file.  If `die_on_error` flag is true, it dies if
-       a lock is already taken for the file; otherwise it
-       returns a negative integer to the caller on failure.
+       Take a pointer to `struct lock_file`, the path of the file to
+       be locked (e.g. `$GIT_DIR/index`) and a flags argument (see
+       above). Attempt to create a lockfile for the destination and
+       return the file descriptor for writing to the file.
+
+hold_lock_file_for_append::
+
+       Like `hold_lock_file_for_update`, but before returning copy
+       the existing contents of the file (if any) to the lockfile and
+       position its write pointer at the end of the file.
+
+fdopen_lock_file::
+
+       Associate a stdio stream with the lockfile. Return NULL
+       (*without* rolling back the lockfile) on error. The stream is
+       closed automatically when `close_lock_file` is called or when
+       the file is committed or rolled back.
+
+get_locked_file_path::
+
+       Return the path of the file that is locked by the specified
+       lock_file object. The caller must free the memory.
 
 commit_lock_file::
 
-       Take a pointer to the `struct lock_file` initialized
-       with an earlier call to `hold_lock_file_for_update()`,
-       close the file descriptor and rename the lockfile to its
-       final destination.  Returns 0 upon success, a negative
-       value on failure to close(2) or rename(2).
+       Take a pointer to the `struct lock_file` initialized with an
+       earlier call to `hold_lock_file_for_update` or
+       `hold_lock_file_for_append`, close the file descriptor, and
+       rename the lockfile to its final destination. Return 0 upon
+       success. On failure, roll back the lock file and return -1,
+       with `errno` set to the value from the failing call to
+       `close(2)` or `rename(2)`. It is a bug to call
+       `commit_lock_file` for a `lock_file` object that is not
+       currently locked.
+
+commit_lock_file_to::
+
+       Like `commit_lock_file()`, except that it takes an explicit
+       `path` argument to which the lockfile should be renamed. The
+       `path` must be on the same filesystem as the lock file.
 
 rollback_lock_file::
 
-       Take a pointer to the `struct lock_file` initialized
-       with an earlier call to `hold_lock_file_for_update()`,
-       close the file descriptor and remove the lockfile.
+       Take a pointer to the `struct lock_file` initialized with an
+       earlier call to `hold_lock_file_for_update` or
+       `hold_lock_file_for_append`, close the file descriptor and
+       remove the lockfile. It is a NOOP to call
+       `rollback_lock_file()` for a `lock_file` object that has
+       already been committed or rolled back.
 
 close_lock_file::
-       Take a pointer to the `struct lock_file` initialized
-       with an earlier call to `hold_lock_file_for_update()`,
-       and close the file descriptor.  Returns 0 upon success,
-       a negative value on failure to close(2).
-
-Because the structure is used in an `atexit(3)` handler, its
-storage has to stay throughout the life of the program.  It
-cannot be an auto variable allocated on the stack.
-
-Call `commit_lock_file()` or `rollback_lock_file()` when you are
-done writing to the file descriptor.  If you do not call either
-and simply `exit(3)` from the program, an `atexit(3)` handler
-will close and remove the lockfile.
-
-If you need to close the file descriptor you obtained from
-`hold_lock_file_for_update` function yourself, do so by calling
-`close_lock_file()`.  You should never call `close(2)` yourself!
-Otherwise the `struct
-lock_file` structure still remembers that the file descriptor
-needs to be closed, and a later call to `commit_lock_file()` or
-`rollback_lock_file()` will result in duplicate calls to
-`close(2)`.  Worse yet, if you `close(2)`, open another file
-descriptor for completely different purpose, and then call
-`commit_lock_file()` or `rollback_lock_file()`, they may close
-that unrelated file descriptor.
+
+       Take a pointer to the `struct lock_file` initialized with an
+       earlier call to `hold_lock_file_for_update` or
+       `hold_lock_file_for_append`. Close the file descriptor (and
+       the file pointer if it has been opened using
+       `fdopen_lock_file`). Return 0 upon success. On failure to
+       `close(2)`, return a negative value and roll back the lock
+       file. Usually `commit_lock_file`, `commit_lock_file_to`, or
+       `rollback_lock_file` should eventually be called if
+       `close_lock_file` succeeds.
+
+reopen_lock_file::
+
+       Re-open a lockfile that has been closed (using
+       `close_lock_file`) but not yet committed or rolled back. This
+       can be used to implement a sequence of operations like the
+       following:
+
+       * Lock file.
+
+       * Write new contents to lockfile, then `close_lock_file` to
+         cause the contents to be written to disk.
+
+       * Pass the name of the lockfile to another program to allow it
+         (and nobody else) to inspect the contents you wrote, while
+         still holding the lock yourself.
+
+       * `reopen_lock_file` to reopen the lockfile. Make further
+         updates to the contents.
+
+       * `commit_lock_file` to make the final version permanent.
index 569c48a352b76ab3fc0d31f21f0f1a1bd654f41d..462e20645f1ea87dcc04938b8ca504bd0d7d7636 100644 (file)
@@ -212,9 +212,9 @@ out of what the server said it could do with the first 'want' line.
   want-list         =  first-want
                       *additional-want
 
-  shallow-line      =  PKT_LINE("shallow" SP obj-id)
+  shallow-line      =  PKT-LINE("shallow" SP obj-id)
 
-  depth-request     =  PKT_LINE("deepen" SP depth)
+  depth-request     =  PKT-LINE("deepen" SP depth)
 
   first-want        =  PKT-LINE("want" SP obj-id SP capability-list LF)
   additional-want   =  PKT-LINE("want" SP obj-id LF)
@@ -465,7 +465,7 @@ contain all the objects that the server will need to complete the new
 references.
 
 ----
-  update-request    =  *shallow command-list [pack-file]
+  update-request    =  *shallow ( command-list | push-cert ) [pack-file]
 
   shallow           =  PKT-LINE("shallow" SP obj-id LF)
 
@@ -481,12 +481,27 @@ references.
   old-id            =  obj-id
   new-id            =  obj-id
 
+  push-cert         = PKT-LINE("push-cert" NUL capability-list LF)
+                     PKT-LINE("certificate version 0.1" LF)
+                     PKT-LINE("pusher" SP ident LF)
+                     PKT-LINE("pushee" SP url LF)
+                     PKT-LINE("nonce" SP nonce LF)
+                     PKT-LINE(LF)
+                     *PKT-LINE(command LF)
+                     *PKT-LINE(gpg-signature-lines LF)
+                     PKT-LINE("push-cert-end" LF)
+
   pack-file         = "PACK" 28*(OCTET)
 ----
 
 If the receiving end does not support delete-refs, the sending end MUST
 NOT ask for delete command.
 
+If the receiving end does not support push-cert, the sending end
+MUST NOT send a push-cert command.  When a push-cert command is
+sent, command-list MUST NOT be sent; the commands recorded in the
+push certificate is used instead.
+
 The pack-file MUST NOT be sent if the only command used is 'delete'.
 
 A pack-file MUST be sent if either create or update command is used,
@@ -501,6 +516,34 @@ was being processed (the obj-id is still the same as the old-id), and
 it will run any update hooks to make sure that the update is acceptable.
 If all of that is fine, the server will then update the references.
 
+Push Certificate
+----------------
+
+A push certificate begins with a set of header lines.  After the
+header and an empty line, the protocol commands follow, one per
+line.
+
+Currently, the following header fields are defined:
+
+`pusher` ident::
+       Identify the GPG key in "Human Readable Name <email@address>"
+       format.
+
+`pushee` url::
+       The repository URL (anonymized, if the URL contains
+       authentication material) the user who ran `git push`
+       intended to push into.
+
+`nonce` nonce::
+       The 'nonce' string the receiving repository asked the
+       pushing user to include in the certificate, to prevent
+       replay attacks.
+
+The GPG signature lines are a detached signature for the contents
+recorded in the push certificate before the signature block begins.
+The detached signature is used to certify that the commands were
+given by the pusher, who must be the signer.
+
 Report Status
 -------------
 
index e17434384724662aabb60d7fceed40a433207608..0c92deebcca2296c4938e7b2ae1733e35b2ce9c4 100644 (file)
@@ -18,8 +18,8 @@ was sent.  Server MUST NOT ignore capabilities that client requested
 and server advertised.  As a consequence of these rules, server MUST
 NOT advertise capabilities it does not understand.
 
-The 'report-status', 'delete-refs', and 'quiet' capabilities are sent and
-recognized by the receive-pack (push to server) process.
+The 'report-status', 'delete-refs', 'quiet', and 'push-cert' capabilities
+are sent and recognized by the receive-pack (push to server) process.
 
 The 'ofs-delta' and 'side-band-64k' capabilities are sent and recognized
 by both upload-pack and receive-pack protocols.  The 'agent' capability
@@ -250,3 +250,12 @@ allow-tip-sha1-in-want
 If the upload-pack server advertises this capability, fetch-pack may
 send "want" lines with SHA-1s that exist at the server but are not
 advertised by upload-pack.
+
+push-cert=<nonce>
+-----------------
+
+The receive-pack server that advertises this capability is willing
+to accept a signed push certificate, and asks the <nonce> to be
+included in the push certificate.  A send-pack client MUST NOT
+send a push-cert packet unless the receive-pack server advertises
+this capability.
index f34a2d4cb82a907c2a1a9a8d760cfd3a1788f6e3..356feb5c86023d00a41ac8bca7d44f9779c39ad0 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -568,6 +568,7 @@ TEST_PROGRAMS_NEED_X += test-revision-walking
 TEST_PROGRAMS_NEED_X += test-run-command
 TEST_PROGRAMS_NEED_X += test-scrap-cache-tree
 TEST_PROGRAMS_NEED_X += test-sha1
+TEST_PROGRAMS_NEED_X += test-sha1-array
 TEST_PROGRAMS_NEED_X += test-sigchain
 TEST_PROGRAMS_NEED_X += test-string-list
 TEST_PROGRAMS_NEED_X += test-subprocess
index 952a659bcb59ea8b476d6677f2a04d717c6c7dd2..94a9981adf72c437f8660c66b67506b50ed8d3a9 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -5,6 +5,7 @@
 #include "archive.h"
 #include "parse-options.h"
 #include "unpack-trees.h"
+#include "dir.h"
 
 static char const * const archive_usage[] = {
        N_("git archive [options] <tree-ish> [<path>...]"),
@@ -98,9 +99,19 @@ static void setup_archive_check(struct git_attr_check *check)
        check[1].attr = attr_export_subst;
 }
 
+struct directory {
+       struct directory *up;
+       unsigned char sha1[20];
+       int baselen, len;
+       unsigned mode;
+       int stage;
+       char path[FLEX_ARRAY];
+};
+
 struct archiver_context {
        struct archiver_args *args;
        write_archive_entry_fn_t write_entry;
+       struct directory *bottom;
 };
 
 static int write_archive_entry(const unsigned char *sha1, const char *base,
@@ -146,6 +157,65 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
        return write_entry(args, sha1, path.buf, path.len, mode);
 }
 
+static void queue_directory(const unsigned char *sha1,
+               const char *base, int baselen, const char *filename,
+               unsigned mode, int stage, struct archiver_context *c)
+{
+       struct directory *d;
+       d = xmallocz(sizeof(*d) + baselen + 1 + strlen(filename));
+       d->up      = c->bottom;
+       d->baselen = baselen;
+       d->mode    = mode;
+       d->stage   = stage;
+       c->bottom  = d;
+       d->len = sprintf(d->path, "%.*s%s/", baselen, base, filename);
+       hashcpy(d->sha1, sha1);
+}
+
+static int write_directory(struct archiver_context *c)
+{
+       struct directory *d = c->bottom;
+       int ret;
+
+       if (!d)
+               return 0;
+       c->bottom = d->up;
+       d->path[d->len - 1] = '\0'; /* no trailing slash */
+       ret =
+               write_directory(c) ||
+               write_archive_entry(d->sha1, d->path, d->baselen,
+                                   d->path + d->baselen, d->mode,
+                                   d->stage, c) != READ_TREE_RECURSIVE;
+       free(d);
+       return ret ? -1 : 0;
+}
+
+static int queue_or_write_archive_entry(const unsigned char *sha1,
+               const char *base, int baselen, const char *filename,
+               unsigned mode, int stage, void *context)
+{
+       struct archiver_context *c = context;
+
+       while (c->bottom &&
+              !(baselen >= c->bottom->len &&
+                !strncmp(base, c->bottom->path, c->bottom->len))) {
+               struct directory *next = c->bottom->up;
+               free(c->bottom);
+               c->bottom = next;
+       }
+
+       if (S_ISDIR(mode)) {
+               queue_directory(sha1, base, baselen, filename,
+                               mode, stage, c);
+               return READ_TREE_RECURSIVE;
+       }
+
+       if (write_directory(c))
+               return -1;
+       return write_archive_entry(sha1, base, baselen, filename, mode,
+                                  stage, context);
+}
+
 int write_archive_entries(struct archiver_args *args,
                write_archive_entry_fn_t write_entry)
 {
@@ -167,6 +237,7 @@ int write_archive_entries(struct archiver_args *args,
                        return err;
        }
 
+       memset(&context, 0, sizeof(context));
        context.args = args;
        context.write_entry = write_entry;
 
@@ -187,9 +258,17 @@ int write_archive_entries(struct archiver_args *args,
        }
 
        err = read_tree_recursive(args->tree, "", 0, 0, &args->pathspec,
-                                 write_archive_entry, &context);
+                                 args->pathspec.has_wildcard ?
+                                 queue_or_write_archive_entry :
+                                 write_archive_entry,
+                                 &context);
        if (err == READ_TREE_RECURSIVE)
                err = 0;
+       while (context.bottom) {
+               struct directory *next = context.bottom->up;
+               free(context.bottom);
+               context.bottom = next;
+       }
        return err;
 }
 
@@ -211,7 +290,16 @@ static int reject_entry(const unsigned char *sha1, const char *base,
                        int baselen, const char *filename, unsigned mode,
                        int stage, void *context)
 {
-       return -1;
+       int ret = -1;
+       if (S_ISDIR(mode)) {
+               struct strbuf sb = STRBUF_INIT;
+               strbuf_addstr(&sb, base);
+               strbuf_addstr(&sb, filename);
+               if (!match_pathspec(context, sb.buf, sb.len, 0, NULL, 1))
+                       ret = READ_TREE_RECURSIVE;
+               strbuf_release(&sb);
+       }
+       return ret;
 }
 
 static int path_exists(struct tree *tree, const char *path)
@@ -221,7 +309,9 @@ static int path_exists(struct tree *tree, const char *path)
        int ret;
 
        parse_pathspec(&pathspec, 0, 0, "", paths);
-       ret = read_tree_recursive(tree, "", 0, 0, &pathspec, reject_entry, NULL);
+       pathspec.recursive = 1;
+       ret = read_tree_recursive(tree, "", 0, 0, &pathspec,
+                                 reject_entry, &pathspec);
        free_pathspec(&pathspec);
        return ret != 0;
 }
@@ -237,6 +327,7 @@ static void parse_pathspec_arg(const char **pathspec,
        parse_pathspec(&ar_args->pathspec, 0,
                       PATHSPEC_PREFER_FULL,
                       "", pathspec);
+       ar_args->pathspec.recursive = 1;
        if (pathspec) {
                while (*pathspec) {
                        if (**pathspec && !path_exists(ar_args->tree, *pathspec))
index 352b85e8db19e1b4fa05ca37804f0c625e582912..ae6d3e262bcbb3dc57c0ddf146791380259b31be 100644 (file)
@@ -5,6 +5,7 @@
  */
 #include "cache.h"
 #include "builtin.h"
+#include "lockfile.h"
 #include "dir.h"
 #include "pathspec.h"
 #include "exec_cmd.h"
index 97f7e8e499a69d7cb34be723a2f881365aa90347..6696ea4c3ffd4f7af04f3f56609a78a74a0a0b55 100644 (file)
@@ -7,6 +7,7 @@
  *
  */
 #include "cache.h"
+#include "lockfile.h"
 #include "cache-tree.h"
 #include "quote.h"
 #include "blob.h"
index 05edd9e1df52e63a1644cddc0fb0e6a0d764d90b..383dccf93ef4eceb873eee4cd63e6781854b295c 100644 (file)
@@ -5,7 +5,7 @@
  *
  */
 #include "builtin.h"
-#include "cache.h"
+#include "lockfile.h"
 #include "quote.h"
 #include "cache-tree.h"
 #include "parse-options.h"
index cef1996d1663fb2f5d4b910bfd1661ebe2fe042b..b4decd5b19c6ec9399f80c7a063343331fe8fd38 100644 (file)
@@ -1,5 +1,5 @@
-#include "cache.h"
 #include "builtin.h"
+#include "lockfile.h"
 #include "parse-options.h"
 #include "refs.h"
 #include "commit.h"
index 3927edfb6ede269929dc704cbb928a7516a8a0a7..d3bf9532d60fd52dfb440fc991e91544e8353fbe 100644 (file)
@@ -9,6 +9,7 @@
  */
 
 #include "builtin.h"
+#include "lockfile.h"
 #include "parse-options.h"
 #include "fetch-pack.h"
 #include "refs.h"
index cff7802f8f32a174c91e2182f224496f702740b3..81dc622a3b72040daca199f05d0a3322f316e6f6 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include "cache.h"
+#include "lockfile.h"
 #include "cache-tree.h"
 #include "color.h"
 #include "dir.h"
@@ -315,8 +316,8 @@ static void refresh_cache_or_die(int refresh_flags)
                die_resolve_conflict("commit");
 }
 
-static char *prepare_index(int argc, const char **argv, const char *prefix,
-                          const struct commit *current_head, int is_status)
+static const char *prepare_index(int argc, const char **argv, const char *prefix,
+                                const struct commit *current_head, int is_status)
 {
        struct string_list partial;
        struct pathspec pathspec;
@@ -341,7 +342,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
                        die(_("unable to create temporary index"));
 
                old_index_env = getenv(INDEX_ENVIRONMENT);
-               setenv(INDEX_ENVIRONMENT, index_lock.filename, 1);
+               setenv(INDEX_ENVIRONMENT, index_lock.filename.buf, 1);
 
                if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
                        die(_("interactive add failed"));
@@ -352,7 +353,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
                        unsetenv(INDEX_ENVIRONMENT);
 
                discard_cache();
-               read_cache_from(index_lock.filename);
+               read_cache_from(index_lock.filename.buf);
                if (update_main_cache_tree(WRITE_TREE_SILENT) == 0) {
                        if (reopen_lock_file(&index_lock) < 0)
                                die(_("unable to write index file"));
@@ -362,7 +363,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
                        warning(_("Failed to update main cache tree"));
 
                commit_style = COMMIT_NORMAL;
-               return index_lock.filename;
+               return index_lock.filename.buf;
        }
 
        /*
@@ -385,7 +386,7 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
                if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
                        die(_("unable to write new_index file"));
                commit_style = COMMIT_NORMAL;
-               return index_lock.filename;
+               return index_lock.filename.buf;
        }
 
        /*
@@ -472,9 +473,9 @@ static char *prepare_index(int argc, const char **argv, const char *prefix,
                die(_("unable to write temporary index file"));
 
        discard_cache();
-       read_cache_from(false_lock.filename);
+       read_cache_from(false_lock.filename.buf);
 
-       return false_lock.filename;
+       return false_lock.filename.buf;
 }
 
 static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
index ee6a3b998f2c22b1d131a180e77079117e598136..9103193b4f65dc9e224051fb1e19eda6d9c7ae6a 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "commit.h"
 #include "tag.h"
 #include "refs.h"
index 0f247d24008a4dc7ba5958e716c13f7582a820f1..4326fa56bfa2021c6518c225eaa3f89b8b852e11 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (c) 2006 Junio C Hamano
  */
 #include "cache.h"
+#include "lockfile.h"
 #include "color.h"
 #include "commit.h"
 #include "blob.h"
index ced1456e1e3b71af05e382fcd505cc269391bb88..005adbebea80a83a8d0dbb1de744cb50bf689de6 100644 (file)
@@ -11,7 +11,7 @@
  */
 
 #include "builtin.h"
-#include "cache.h"
+#include "lockfile.h"
 #include "parse-options.h"
 #include "run-command.h"
 #include "sigchain.h"
index 763cda098cf1e2ac2ccbae45d3602f8bb1093be0..8e02ea109ac8f9fd739c4d54b7a709916f88022a 100644 (file)
@@ -59,7 +59,6 @@ static int split_one(FILE *mbox, const char *name, int allow_bare)
        int is_bare = !is_from_line(buf.buf, buf.len);
 
        if (is_bare && !allow_bare) {
-               unlink(name);
                fprintf(stderr, "corrupt mailbox\n");
                exit(1);
        }
index dff043dac33ea88a8c5a05ea5ac3da71ca00c0d3..4513fadc5fc3c631591d1a9b65ec939ada5d2de3 100644 (file)
@@ -9,6 +9,7 @@
 #include "cache.h"
 #include "parse-options.h"
 #include "builtin.h"
+#include "lockfile.h"
 #include "run-command.h"
 #include "diff.h"
 #include "refs.h"
@@ -656,19 +657,18 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                              struct commit_list *remoteheads,
                              struct commit *head, const char *head_arg)
 {
-       struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
+       static struct lock_file lock;
 
-       hold_locked_index(lock, 1);
+       hold_locked_index(&lock, 1);
        refresh_cache(REFRESH_QUIET);
        if (active_cache_changed &&
-           write_locked_index(&the_index, lock, COMMIT_LOCK))
+           write_locked_index(&the_index, &lock, COMMIT_LOCK))
                return error(_("Unable to write index."));
-       rollback_lock_file(lock);
+       rollback_lock_file(&lock);
 
        if (!strcmp(strategy, "recursive") || !strcmp(strategy, "subtree")) {
                int clean, x;
                struct commit *result;
-               struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
                struct commit_list *reversed = NULL;
                struct merge_options o;
                struct commit_list *j;
@@ -696,13 +696,13 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
                for (j = common; j; j = j->next)
                        commit_list_insert(j->item, &reversed);
 
-               hold_locked_index(lock, 1);
+               hold_locked_index(&lock, 1);
                clean = merge_recursive(&o, head,
                                remoteheads->item, reversed, &result);
                if (active_cache_changed &&
-                   write_locked_index(&the_index, lock, COMMIT_LOCK))
+                   write_locked_index(&the_index, &lock, COMMIT_LOCK))
                        die (_("unable to write %s"), get_index_file());
-               rollback_lock_file(lock);
+               rollback_lock_file(&lock);
                return clean ? 0 : 1;
        } else {
                return try_merge_command(strategy, xopts_nr, xopts,
index 8883baa903e92093cabcc3dbaac7ccec3eecd64a..563d05ba1af21c9ae73edceba060161969434c76 100644 (file)
@@ -3,8 +3,8 @@
  *
  * Copyright (C) 2006 Johannes Schindelin
  */
-#include "cache.h"
 #include "builtin.h"
+#include "lockfile.h"
 #include "dir.h"
 #include "cache-tree.h"
 #include "string-list.h"
index f50e3d5e77db6842ff3450560aad330f6a4bd91d..ae56f73a66edde6ee8c8b6be349d9e31f29699dc 100644 (file)
@@ -506,6 +506,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                OPT_BIT(0, "no-verify", &flags, N_("bypass pre-push hook"), TRANSPORT_PUSH_NO_HOOK),
                OPT_BIT(0, "follow-tags", &flags, N_("push missing but relevant tags"),
                        TRANSPORT_PUSH_FOLLOW_TAGS),
+               OPT_BIT(0, "signed", &flags, N_("GPG sign the push"), TRANSPORT_PUSH_CERT),
                OPT_END()
        };
 
index e7e1c33a7fb1f1eb7828f4d632fb492f128f66fe..43b47f72f1deffa8fa5e3fa5ac403d4c4a819355 100644 (file)
@@ -5,6 +5,7 @@
  */
 
 #include "cache.h"
+#include "lockfile.h"
 #include "object.h"
 #include "tree.h"
 #include "tree-walk.h"
index daf0600ca30eb3969e0583cc1096e6f33291d4aa..f2f6c67359cb5941c2d57b6692ce1fbbc86edccc 100644 (file)
@@ -1,4 +1,5 @@
 #include "builtin.h"
+#include "lockfile.h"
 #include "pack.h"
 #include "refs.h"
 #include "pkt-line.h"
@@ -15,6 +16,8 @@
 #include "connected.h"
 #include "argv-array.h"
 #include "version.h"
+#include "tag.h"
+#include "gpg-interface.h"
 #include "sigchain.h"
 
 static const char receive_pack_usage[] = "git receive-pack <git-dir>";
@@ -42,11 +45,27 @@ static int prefer_ofs_delta = 1;
 static int auto_update_server_info;
 static int auto_gc = 1;
 static int fix_thin = 1;
+static int stateless_rpc;
+static const char *service_dir;
 static const char *head_name;
 static void *head_name_to_free;
 static int sent_capabilities;
 static int shallow_update;
 static const char *alt_shallow_file;
+static struct strbuf push_cert = STRBUF_INIT;
+static unsigned char push_cert_sha1[20];
+static struct signature_check sigcheck;
+static const char *push_cert_nonce;
+static const char *cert_nonce_seed;
+
+static const char *NONCE_UNSOLICITED = "UNSOLICITED";
+static const char *NONCE_BAD = "BAD";
+static const char *NONCE_MISSING = "MISSING";
+static const char *NONCE_OK = "OK";
+static const char *NONCE_SLOP = "SLOP";
+static const char *nonce_status;
+static long nonce_stamp_slop;
+static unsigned long nonce_stamp_slop_limit;
 
 static enum deny_action parse_deny_action(const char *var, const char *value)
 {
@@ -130,6 +149,14 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (strcmp(var, "receive.certnonceseed") == 0)
+               return git_config_string(&cert_nonce_seed, var, value);
+
+       if (strcmp(var, "receive.certnonceslop") == 0) {
+               nonce_stamp_slop_limit = git_config_ulong(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, cb);
 }
 
@@ -138,15 +165,23 @@ static void show_ref(const char *path, const unsigned char *sha1)
        if (ref_is_hidden(path))
                return;
 
-       if (sent_capabilities)
+       if (sent_capabilities) {
                packet_write(1, "%s %s\n", sha1_to_hex(sha1), path);
-       else
-               packet_write(1, "%s %s%c%s%s agent=%s\n",
-                            sha1_to_hex(sha1), path, 0,
-                            " report-status delete-refs side-band-64k quiet",
-                            prefer_ofs_delta ? " ofs-delta" : "",
-                            git_user_agent_sanitized());
-       sent_capabilities = 1;
+       } else {
+               struct strbuf cap = STRBUF_INIT;
+
+               strbuf_addstr(&cap,
+                             "report-status delete-refs side-band-64k quiet");
+               if (prefer_ofs_delta)
+                       strbuf_addstr(&cap, " ofs-delta");
+               if (push_cert_nonce)
+                       strbuf_addf(&cap, " push-cert=%s", push_cert_nonce);
+               strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
+               packet_write(1, "%s %s%c%s\n",
+                            sha1_to_hex(sha1), path, 0, cap.buf);
+               strbuf_release(&cap);
+               sent_capabilities = 1;
+       }
 }
 
 static int show_ref_cb(const char *path, const unsigned char *sha1, int flag, void *unused)
@@ -253,6 +288,222 @@ static int copy_to_sideband(int in, int out, void *arg)
        return 0;
 }
 
+#define HMAC_BLOCK_SIZE 64
+
+static void hmac_sha1(unsigned char *out,
+                     const char *key_in, size_t key_len,
+                     const char *text, size_t text_len)
+{
+       unsigned char key[HMAC_BLOCK_SIZE];
+       unsigned char k_ipad[HMAC_BLOCK_SIZE];
+       unsigned char k_opad[HMAC_BLOCK_SIZE];
+       int i;
+       git_SHA_CTX ctx;
+
+       /* RFC 2104 2. (1) */
+       memset(key, '\0', HMAC_BLOCK_SIZE);
+       if (HMAC_BLOCK_SIZE < key_len) {
+               git_SHA1_Init(&ctx);
+               git_SHA1_Update(&ctx, key_in, key_len);
+               git_SHA1_Final(key, &ctx);
+       } else {
+               memcpy(key, key_in, key_len);
+       }
+
+       /* RFC 2104 2. (2) & (5) */
+       for (i = 0; i < sizeof(key); i++) {
+               k_ipad[i] = key[i] ^ 0x36;
+               k_opad[i] = key[i] ^ 0x5c;
+       }
+
+       /* RFC 2104 2. (3) & (4) */
+       git_SHA1_Init(&ctx);
+       git_SHA1_Update(&ctx, k_ipad, sizeof(k_ipad));
+       git_SHA1_Update(&ctx, text, text_len);
+       git_SHA1_Final(out, &ctx);
+
+       /* RFC 2104 2. (6) & (7) */
+       git_SHA1_Init(&ctx);
+       git_SHA1_Update(&ctx, k_opad, sizeof(k_opad));
+       git_SHA1_Update(&ctx, out, 20);
+       git_SHA1_Final(out, &ctx);
+}
+
+static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
+{
+       struct strbuf buf = STRBUF_INIT;
+       unsigned char sha1[20];
+
+       strbuf_addf(&buf, "%s:%lu", path, stamp);
+       hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));;
+       strbuf_release(&buf);
+
+       /* RFC 2104 5. HMAC-SHA1-80 */
+       strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1));
+       return strbuf_detach(&buf, NULL);
+}
+
+/*
+ * NEEDSWORK: reuse find_commit_header() from jk/commit-author-parsing
+ * after dropping "_commit" from its name and possibly moving it out
+ * of commit.c
+ */
+static char *find_header(const char *msg, size_t len, const char *key)
+{
+       int key_len = strlen(key);
+       const char *line = msg;
+
+       while (line && line < msg + len) {
+               const char *eol = strchrnul(line, '\n');
+
+               if ((msg + len <= eol) || line == eol)
+                       return NULL;
+               if (line + key_len < eol &&
+                   !memcmp(line, key, key_len) && line[key_len] == ' ') {
+                       int offset = key_len + 1;
+                       return xmemdupz(line + offset, (eol - line) - offset);
+               }
+               line = *eol ? eol + 1 : NULL;
+       }
+       return NULL;
+}
+
+static const char *check_nonce(const char *buf, size_t len)
+{
+       char *nonce = find_header(buf, len, "nonce");
+       unsigned long stamp, ostamp;
+       char *bohmac, *expect = NULL;
+       const char *retval = NONCE_BAD;
+
+       if (!nonce) {
+               retval = NONCE_MISSING;
+               goto leave;
+       } else if (!push_cert_nonce) {
+               retval = NONCE_UNSOLICITED;
+               goto leave;
+       } else if (!strcmp(push_cert_nonce, nonce)) {
+               retval = NONCE_OK;
+               goto leave;
+       }
+
+       if (!stateless_rpc) {
+               /* returned nonce MUST match what we gave out earlier */
+               retval = NONCE_BAD;
+               goto leave;
+       }
+
+       /*
+        * In stateless mode, we may be receiving a nonce issued by
+        * another instance of the server that serving the same
+        * repository, and the timestamps may not match, but the
+        * nonce-seed and dir should match, so we can recompute and
+        * report the time slop.
+        *
+        * In addition, when a nonce issued by another instance has
+        * timestamp within receive.certnonceslop seconds, we pretend
+        * as if we issued that nonce when reporting to the hook.
+        */
+
+       /* nonce is concat(<seconds-since-epoch>, "-", <hmac>) */
+       if (*nonce <= '0' || '9' < *nonce) {
+               retval = NONCE_BAD;
+               goto leave;
+       }
+       stamp = strtoul(nonce, &bohmac, 10);
+       if (bohmac == nonce || bohmac[0] != '-') {
+               retval = NONCE_BAD;
+               goto leave;
+       }
+
+       expect = prepare_push_cert_nonce(service_dir, stamp);
+       if (strcmp(expect, nonce)) {
+               /* Not what we would have signed earlier */
+               retval = NONCE_BAD;
+               goto leave;
+       }
+
+       /*
+        * By how many seconds is this nonce stale?  Negative value
+        * would mean it was issued by another server with its clock
+        * skewed in the future.
+        */
+       ostamp = strtoul(push_cert_nonce, NULL, 10);
+       nonce_stamp_slop = (long)ostamp - (long)stamp;
+
+       if (nonce_stamp_slop_limit &&
+           abs(nonce_stamp_slop) <= nonce_stamp_slop_limit) {
+               /*
+                * Pretend as if the received nonce (which passes the
+                * HMAC check, so it is not a forged by third-party)
+                * is what we issued.
+                */
+               free((void *)push_cert_nonce);
+               push_cert_nonce = xstrdup(nonce);
+               retval = NONCE_OK;
+       } else {
+               retval = NONCE_SLOP;
+       }
+
+leave:
+       free(nonce);
+       free(expect);
+       return retval;
+}
+
+static void prepare_push_cert_sha1(struct child_process *proc)
+{
+       static int already_done;
+       struct argv_array env = ARGV_ARRAY_INIT;
+
+       if (!push_cert.len)
+               return;
+
+       if (!already_done) {
+               struct strbuf gpg_output = STRBUF_INIT;
+               struct strbuf gpg_status = STRBUF_INIT;
+               int bogs /* beginning_of_gpg_sig */;
+
+               already_done = 1;
+               if (write_sha1_file(push_cert.buf, push_cert.len, "blob", push_cert_sha1))
+                       hashclr(push_cert_sha1);
+
+               memset(&sigcheck, '\0', sizeof(sigcheck));
+               sigcheck.result = 'N';
+
+               bogs = parse_signature(push_cert.buf, push_cert.len);
+               if (verify_signed_buffer(push_cert.buf, bogs,
+                                        push_cert.buf + bogs, push_cert.len - bogs,
+                                        &gpg_output, &gpg_status) < 0) {
+                       ; /* error running gpg */
+               } else {
+                       sigcheck.payload = push_cert.buf;
+                       sigcheck.gpg_output = gpg_output.buf;
+                       sigcheck.gpg_status = gpg_status.buf;
+                       parse_gpg_output(&sigcheck);
+               }
+
+               strbuf_release(&gpg_output);
+               strbuf_release(&gpg_status);
+               nonce_status = check_nonce(push_cert.buf, bogs);
+       }
+       if (!is_null_sha1(push_cert_sha1)) {
+               argv_array_pushf(&env, "GIT_PUSH_CERT=%s", sha1_to_hex(push_cert_sha1));
+               argv_array_pushf(&env, "GIT_PUSH_CERT_SIGNER=%s",
+                                sigcheck.signer ? sigcheck.signer : "");
+               argv_array_pushf(&env, "GIT_PUSH_CERT_KEY=%s",
+                                sigcheck.key ? sigcheck.key : "");
+               argv_array_pushf(&env, "GIT_PUSH_CERT_STATUS=%c", sigcheck.result);
+               if (push_cert_nonce) {
+                       argv_array_pushf(&env, "GIT_PUSH_CERT_NONCE=%s", push_cert_nonce);
+                       argv_array_pushf(&env, "GIT_PUSH_CERT_NONCE_STATUS=%s", nonce_status);
+                       if (nonce_status == NONCE_SLOP)
+                               argv_array_pushf(&env, "GIT_PUSH_CERT_NONCE_SLOP=%ld",
+                                                nonce_stamp_slop);
+               }
+               proc->env = env.argv;
+       }
+}
+
 typedef int (*feed_fn)(void *, const char **, size_t *);
 static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_state)
 {
@@ -271,6 +522,8 @@ static int run_and_feed_hook(const char *hook_name, feed_fn feed, void *feed_sta
        proc.in = -1;
        proc.stdout_to_stderr = 1;
 
+       prepare_push_cert_sha1(&proc);
+
        if (use_sideband) {
                memset(&muxer, 0, sizeof(muxer));
                muxer.proc = copy_to_sideband;
@@ -841,40 +1094,79 @@ static void execute_commands(struct command *commands,
                      "the reported refs above");
 }
 
+static struct command **queue_command(struct command **tail,
+                                     const char *line,
+                                     int linelen)
+{
+       unsigned char old_sha1[20], new_sha1[20];
+       struct command *cmd;
+       const char *refname;
+       int reflen;
+
+       if (linelen < 83 ||
+           line[40] != ' ' ||
+           line[81] != ' ' ||
+           get_sha1_hex(line, old_sha1) ||
+           get_sha1_hex(line + 41, new_sha1))
+               die("protocol error: expected old/new/ref, got '%s'", line);
+
+       refname = line + 82;
+       reflen = linelen - 82;
+       cmd = xcalloc(1, sizeof(struct command) + reflen + 1);
+       hashcpy(cmd->old_sha1, old_sha1);
+       hashcpy(cmd->new_sha1, new_sha1);
+       memcpy(cmd->ref_name, refname, reflen);
+       cmd->ref_name[reflen] = '\0';
+       *tail = cmd;
+       return &cmd->next;
+}
+
+static void queue_commands_from_cert(struct command **tail,
+                                    struct strbuf *push_cert)
+{
+       const char *boc, *eoc;
+
+       if (*tail)
+               die("protocol error: got both push certificate and unsigned commands");
+
+       boc = strstr(push_cert->buf, "\n\n");
+       if (!boc)
+               die("malformed push certificate %.*s", 100, push_cert->buf);
+       else
+               boc += 2;
+       eoc = push_cert->buf + parse_signature(push_cert->buf, push_cert->len);
+
+       while (boc < eoc) {
+               const char *eol = memchr(boc, '\n', eoc - boc);
+               tail = queue_command(tail, boc, eol ? eol - boc : eoc - eol);
+               boc = eol ? eol + 1 : eoc;
+       }
+}
+
 static struct command *read_head_info(struct sha1_array *shallow)
 {
        struct command *commands = NULL;
        struct command **p = &commands;
        for (;;) {
                char *line;
-               unsigned char old_sha1[20], new_sha1[20];
-               struct command *cmd;
-               char *refname;
-               int len, reflen;
+               int len, linelen;
 
                line = packet_read_line(0, &len);
                if (!line)
                        break;
 
                if (len == 48 && starts_with(line, "shallow ")) {
-                       if (get_sha1_hex(line + 8, old_sha1))
-                               die("protocol error: expected shallow sha, got '%s'", line + 8);
-                       sha1_array_append(shallow, old_sha1);
+                       unsigned char sha1[20];
+                       if (get_sha1_hex(line + 8, sha1))
+                               die("protocol error: expected shallow sha, got '%s'",
+                                   line + 8);
+                       sha1_array_append(shallow, sha1);
                        continue;
                }
 
-               if (len < 83 ||
-                   line[40] != ' ' ||
-                   line[81] != ' ' ||
-                   get_sha1_hex(line, old_sha1) ||
-                   get_sha1_hex(line + 41, new_sha1))
-                       die("protocol error: expected old/new/ref, got '%s'",
-                           line);
-
-               refname = line + 82;
-               reflen = strlen(refname);
-               if (reflen + 82 < len) {
-                       const char *feature_list = refname + reflen + 1;
+               linelen = strlen(line);
+               if (linelen < len) {
+                       const char *feature_list = line + linelen + 1;
                        if (parse_feature_request(feature_list, "report-status"))
                                report_status = 1;
                        if (parse_feature_request(feature_list, "side-band-64k"))
@@ -882,13 +1174,34 @@ static struct command *read_head_info(struct sha1_array *shallow)
                        if (parse_feature_request(feature_list, "quiet"))
                                quiet = 1;
                }
-               cmd = xcalloc(1, sizeof(struct command) + len - 80);
-               hashcpy(cmd->old_sha1, old_sha1);
-               hashcpy(cmd->new_sha1, new_sha1);
-               memcpy(cmd->ref_name, line + 82, len - 81);
-               *p = cmd;
-               p = &cmd->next;
+
+               if (!strcmp(line, "push-cert")) {
+                       int true_flush = 0;
+                       char certbuf[1024];
+
+                       for (;;) {
+                               len = packet_read(0, NULL, NULL,
+                                                 certbuf, sizeof(certbuf), 0);
+                               if (!len) {
+                                       true_flush = 1;
+                                       break;
+                               }
+                               if (!strcmp(certbuf, "push-cert-end\n"))
+                                       break; /* end of cert */
+                               strbuf_addstr(&push_cert, certbuf);
+                       }
+
+                       if (true_flush)
+                               break;
+                       continue;
+               }
+
+               p = queue_command(p, line, linelen);
        }
+
+       if (push_cert.len)
+               queue_commands_from_cert(p, &push_cert);
+
        return commands;
 }
 
@@ -1129,9 +1442,7 @@ static int delete_only(struct command *commands)
 int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 {
        int advertise_refs = 0;
-       int stateless_rpc = 0;
        int i;
-       const char *dir = NULL;
        struct command *commands;
        struct sha1_array shallow = SHA1_ARRAY_INIT;
        struct sha1_array ref = SHA1_ARRAY_INIT;
@@ -1164,19 +1475,21 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 
                        usage(receive_pack_usage);
                }
-               if (dir)
+               if (service_dir)
                        usage(receive_pack_usage);
-               dir = arg;
+               service_dir = arg;
        }
-       if (!dir)
+       if (!service_dir)
                usage(receive_pack_usage);
 
        setup_path();
 
-       if (!enter_repo(dir, 0))
-               die("'%s' does not appear to be a git repository", dir);
+       if (!enter_repo(service_dir, 0))
+               die("'%s' does not appear to be a git repository", service_dir);
 
        git_config(receive_pack_config, NULL);
+       if (cert_nonce_seed)
+               push_cert_nonce = prepare_push_cert_nonce(service_dir, time(NULL));
 
        if (0 <= transfer_unpack_limit)
                unpack_limit = transfer_unpack_limit;
@@ -1221,5 +1534,6 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
                packet_flush(1);
        sha1_array_clear(&shallow);
        sha1_array_clear(&ref);
+       free((void *)push_cert_nonce);
        return 0;
 }
index e8a8fb13b9ffc8d3276048131e33e9874bc3bdea..b6388f75b0cffcce03b9eee03be966ce67806310 100644 (file)
@@ -1,5 +1,5 @@
-#include "cache.h"
 #include "builtin.h"
+#include "lockfile.h"
 #include "commit.h"
 #include "refs.h"
 #include "dir.h"
@@ -431,7 +431,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
                         write_str_in_full(lock->lock_fd, "\n") != 1 ||
                         close_ref(lock) < 0)) {
                        status |= error("Couldn't write %s",
-                               lock->lk->filename);
+                                       lock->lk->filename.buf);
                        unlink(newlog_path);
                } else if (rename(newlog_path, log_file)) {
                        status |= error("cannot rename %s to %s",
index 855d478e3b9d2a815ecdcfbf39c261f98ad0994b..4c08ddc1cacc29f2098bb81b434d76f315346dd4 100644 (file)
@@ -8,6 +8,7 @@
  * Copyright (c) 2005, 2006 Linus Torvalds and Junio C Hamano
  */
 #include "builtin.h"
+#include "lockfile.h"
 #include "tag.h"
 #include "object.h"
 #include "commit.h"
index 2b61d3bd41164286fcca3e5fe3c807387d946ff2..d8a9c86dd135e62583f4aeeb3885012da385960a 100644 (file)
@@ -3,8 +3,8 @@
  *
  * Copyright (C) Linus Torvalds 2006
  */
-#include "cache.h"
 #include "builtin.h"
+#include "lockfile.h"
 #include "dir.h"
 #include "cache-tree.h"
 #include "tree-walk.h"
index 4b1bc0fef72c9764a4e4a43bb30f2c820d1aec34..b564a778455c352379127c3f06349e0ebbc81c98 100644 (file)
@@ -154,6 +154,10 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                                args.verbose = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--signed")) {
+                               args.push_cert = 1;
+                               continue;
+                       }
                        if (!strcmp(arg, "--progress")) {
                                progress = 1;
                                continue;
index e8c7fd4d4957b83de42e7af2ef9f2676bfc294b2..b0e3dc91055ec7e7579fb5f1964879a99fd71606 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) Linus Torvalds, 2005
  */
 #include "cache.h"
+#include "lockfile.h"
 #include "quote.h"
 #include "cache-tree.h"
 #include "tree-walk.h"
@@ -942,7 +943,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                if (newfd < 0) {
                        if (refresh_args.flags & REFRESH_QUIET)
                                exit(128);
-                       unable_to_lock_index_die(get_index_file(), lock_error);
+                       unable_to_lock_die(get_index_file(), lock_error);
                }
                if (write_locked_index(&the_index, lock_file, COMMIT_LOCK))
                        die("Unable to write new index file");
index 98e651c284254b3c96c59b4f444cbc0f54edd90e..0c4b8a7cad085fa1c7760e6290f21213830eda3d 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (c) 2011, Google Inc.
  */
+#include "cache.h"
 #include "bulk-checkin.h"
 #include "csum-file.h"
 #include "pack.h"
index 4f599f88414df22856e1a680e021dfe1170e5024..fbd40fc98c955c192a6de698a75b8d56af766f09 100644 (file)
@@ -4,8 +4,6 @@
 #ifndef BULK_CHECKIN_H
 #define BULK_CHECKIN_H
 
-#include "cache.h"
-
 extern int index_bulk_checkin(unsigned char sha1[],
                              int fd, size_t size, enum object_type type,
                              const char *path, unsigned flags);
index b2b89fe8628eb29b613754ee8e837fd56de185da..891a3cacda457fcb83b511ad0f7d07e7feb64cf4 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "bundle.h"
 #include "object.h"
 #include "commit.h"
index 75a54fdc7232e5693b042aa06540d51a99f01551..215202c42d2243ebf704ac083937d65aa8497929 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "tree.h"
 #include "tree-walk.h"
 #include "cache-tree.h"
diff --git a/cache.h b/cache.h
index 8206039cfe9545b4f6eb6757dfd62bf468b3be52..5b8606581563584c2ff274089ad16429a81a1742 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -570,29 +570,11 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
 #define REFRESH_IN_PORCELAIN   0x0020  /* user friendly output, not "needs update" */
 extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
 
-struct lock_file {
-       struct lock_file *next;
-       int fd;
-       pid_t owner;
-       char on_list;
-       char filename[PATH_MAX];
-};
-#define LOCK_DIE_ON_ERROR 1
-#define LOCK_NODEREF 2
-extern int unable_to_lock_error(const char *path, int err);
-extern void unable_to_lock_message(const char *path, int err,
-                                  struct strbuf *buf);
-extern NORETURN void unable_to_lock_index_die(const char *path, int err);
-extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
-extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
-extern int commit_lock_file(struct lock_file *);
-extern int reopen_lock_file(struct lock_file *);
 extern void update_index_if_able(struct index_state *, struct lock_file *);
 
 extern int hold_locked_index(struct lock_file *, int);
 extern void set_alternate_index_output(const char *);
-extern int close_lock_file(struct lock_file *);
-extern void rollback_lock_file(struct lock_file *);
+
 extern int delete_ref(const char *, const unsigned char *sha1, int delopt);
 
 /* Environment bits from configuration mechanism */
@@ -1324,6 +1306,7 @@ extern int git_config_rename_section_in_file(const char *, const char *, const c
 extern const char *git_etc_gitconfig(void);
 extern int check_repository_format_version(const char *var, const char *value, void *cb);
 extern int git_env_bool(const char *, int);
+extern unsigned long git_env_ulong(const char *, unsigned long);
 extern int git_config_system(void);
 extern int config_error_nonbool(const char *);
 #if defined(__GNUC__)
index 9c4439fed61c3b26d493d0f0306b65a471da9b19..19cf8f9c67bd0f4d71172f33424f7d2371a3af68 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1214,42 +1214,6 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header
        free(buf);
 }
 
-static struct {
-       char result;
-       const char *check;
-} sigcheck_gpg_status[] = {
-       { 'G', "\n[GNUPG:] GOODSIG " },
-       { 'B', "\n[GNUPG:] BADSIG " },
-       { 'U', "\n[GNUPG:] TRUST_NEVER" },
-       { 'U', "\n[GNUPG:] TRUST_UNDEFINED" },
-};
-
-static void parse_gpg_output(struct signature_check *sigc)
-{
-       const char *buf = sigc->gpg_status;
-       int i;
-
-       /* Iterate over all search strings */
-       for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
-               const char *found, *next;
-
-               if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
-                       found = strstr(buf, sigcheck_gpg_status[i].check);
-                       if (!found)
-                               continue;
-                       found += strlen(sigcheck_gpg_status[i].check);
-               }
-               sigc->result = sigcheck_gpg_status[i].result;
-               /* The trust messages are not followed by key/signer information */
-               if (sigc->result != 'U') {
-                       sigc->key = xmemdupz(found, 16);
-                       found += 17;
-                       next = strchrnul(found, '\n');
-                       sigc->signer = xmemdupz(found, next - found);
-               }
-       }
-}
-
 void check_commit_signature(const struct commit *commit, struct signature_check *sigc)
 {
        struct strbuf payload = STRBUF_INIT;
index a677eb6c599ddb39f1b4bb7ac44a59bc167fb3a7..15a298357796ffa80f7fb2258e55cf95be0b8895 100644 (file)
--- a/config.c
+++ b/config.c
@@ -6,6 +6,7 @@
  *
  */
 #include "cache.h"
+#include "lockfile.h"
 #include "exec_cmd.h"
 #include "strbuf.h"
 #include "quote.h"
@@ -1139,12 +1140,28 @@ const char *git_etc_gitconfig(void)
        return system_wide;
 }
 
+/*
+ * Parse environment variable 'k' as a boolean (in various
+ * possible spellings); if missing, use the default value 'def'.
+ */
 int git_env_bool(const char *k, int def)
 {
        const char *v = getenv(k);
        return v ? git_config_bool(k, v) : def;
 }
 
+/*
+ * Parse environment variable 'k' as ulong with possibly a unit
+ * suffix; if missing, use the default value 'val'.
+ */
+unsigned long git_env_ulong(const char *k, unsigned long val)
+{
+       const char *v = getenv(k);
+       if (v && !git_parse_ulong(v, &val))
+               die("failed to parse %s", k);
+       return val;
+}
+
 int git_config_system(void)
 {
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
@@ -2024,9 +2041,9 @@ int git_config_set_multivar_in_file(const char *config_filename,
                        MAP_PRIVATE, in_fd, 0);
                close(in_fd);
 
-               if (chmod(lock->filename, st.st_mode & 07777) < 0) {
+               if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
                        error("chmod on %s failed: %s",
-                               lock->filename, strerror(errno));
+                               lock->filename.buf, strerror(errno));
                        ret = CONFIG_NO_WRITE;
                        goto out_free;
                }
@@ -2083,6 +2100,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
        if (commit_lock_file(lock) < 0) {
                error("could not commit config file %s", config_filename);
                ret = CONFIG_NO_WRITE;
+               lock = NULL;
                goto out_free;
        }
 
@@ -2105,7 +2123,7 @@ int git_config_set_multivar_in_file(const char *config_filename,
        return ret;
 
 write_err_out:
-       ret = write_error(lock->filename);
+       ret = write_error(lock->filename.buf);
        goto out_free;
 
 }
@@ -2206,9 +2224,9 @@ int git_config_rename_section_in_file(const char *config_filename,
 
        fstat(fileno(config_file), &st);
 
-       if (chmod(lock->filename, st.st_mode & 07777) < 0) {
+       if (chmod(lock->filename.buf, st.st_mode & 07777) < 0) {
                ret = error("chmod on %s failed: %s",
-                               lock->filename, strerror(errno));
+                               lock->filename.buf, strerror(errno));
                goto out;
        }
 
@@ -2229,7 +2247,7 @@ int git_config_rename_section_in_file(const char *config_filename,
                                }
                                store.baselen = strlen(new_name);
                                if (!store_write_section(out_fd, new_name)) {
-                                       ret = write_error(lock->filename);
+                                       ret = write_error(lock->filename.buf);
                                        goto out;
                                }
                                /*
@@ -2255,7 +2273,7 @@ int git_config_rename_section_in_file(const char *config_filename,
                        continue;
                length = strlen(output);
                if (write_in_full(out_fd, output, length) != length) {
-                       ret = write_error(lock->filename);
+                       ret = write_error(lock->filename.buf);
                        goto out;
                }
        }
index 5ea5b82d2b6c54111c059a1c844c6e9f0b3d3a79..2ed230a861248f510e8e9d4597faef6f14ffd552 100644 (file)
@@ -1467,6 +1467,7 @@ _git_log ()
                        --abbrev-commit --abbrev=
                        --relative-date --date=
                        --pretty= --format= --oneline
+                       --show-signature
                        --cherry-pick
                        --graph
                        --decorate --decorate=
@@ -2344,6 +2345,7 @@ _git_show ()
                ;;
        --*)
                __gitcomp "--pretty= --format= --abbrev-commit --oneline
+                       --show-signature
                        $__git_diff_common_options
                        "
                return
index aa7a139bcf7f3d33511e106545e6d8989a0ffe35..9a5612e93da2058f64bcec20cfd474a8b6b2d223 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -312,11 +312,12 @@ static int crlf_to_worktree(const char *path, const char *src, size_t len,
 struct filter_params {
        const char *src;
        unsigned long size;
+       int fd;
        const char *cmd;
        const char *path;
 };
 
-static int filter_buffer(int in, int out, void *data)
+static int filter_buffer_or_fd(int in, int out, void *data)
 {
        /*
         * Spawn cmd and feed the buffer contents through its stdin.
@@ -354,7 +355,12 @@ static int filter_buffer(int in, int out, void *data)
 
        sigchain_push(SIGPIPE, SIG_IGN);
 
-       write_err = (write_in_full(child_process.in, params->src, params->size) < 0);
+       if (params->src) {
+               write_err = (write_in_full(child_process.in, params->src, params->size) < 0);
+       } else {
+               write_err = copy_fd(params->fd, child_process.in);
+       }
+
        if (close(child_process.in))
                write_err = 1;
        if (write_err)
@@ -370,7 +376,7 @@ static int filter_buffer(int in, int out, void *data)
        return (write_err || status);
 }
 
-static int apply_filter(const char *path, const char *src, size_t len,
+static int apply_filter(const char *path, const char *src, size_t len, int fd,
                         struct strbuf *dst, const char *cmd)
 {
        /*
@@ -391,11 +397,12 @@ static int apply_filter(const char *path, const char *src, size_t len,
                return 1;
 
        memset(&async, 0, sizeof(async));
-       async.proc = filter_buffer;
+       async.proc = filter_buffer_or_fd;
        async.data = &params;
        async.out = -1;
        params.src = src;
        params.size = len;
+       params.fd = fd;
        params.cmd = cmd;
        params.path = path;
 
@@ -746,6 +753,25 @@ static void convert_attrs(struct conv_attrs *ca, const char *path)
        }
 }
 
+int would_convert_to_git_filter_fd(const char *path)
+{
+       struct conv_attrs ca;
+
+       convert_attrs(&ca, path);
+       if (!ca.drv)
+               return 0;
+
+       /*
+        * Apply a filter to an fd only if the filter is required to succeed.
+        * We must die if the filter fails, because the original data before
+        * filtering is not available.
+        */
+       if (!ca.drv->required)
+               return 0;
+
+       return apply_filter(path, NULL, 0, -1, NULL, ca.drv->clean);
+}
+
 int convert_to_git(const char *path, const char *src, size_t len,
                    struct strbuf *dst, enum safe_crlf checksafe)
 {
@@ -760,7 +786,7 @@ int convert_to_git(const char *path, const char *src, size_t len,
                required = ca.drv->required;
        }
 
-       ret |= apply_filter(path, src, len, dst, filter);
+       ret |= apply_filter(path, src, len, -1, dst, filter);
        if (!ret && required)
                die("%s: clean filter '%s' failed", path, ca.drv->name);
 
@@ -777,6 +803,23 @@ int convert_to_git(const char *path, const char *src, size_t len,
        return ret | ident_to_git(path, src, len, dst, ca.ident);
 }
 
+void convert_to_git_filter_fd(const char *path, int fd, struct strbuf *dst,
+                             enum safe_crlf checksafe)
+{
+       struct conv_attrs ca;
+       convert_attrs(&ca, path);
+
+       assert(ca.drv);
+       assert(ca.drv->clean);
+
+       if (!apply_filter(path, NULL, 0, fd, dst, ca.drv->clean))
+               die("%s: clean filter '%s' failed", path, ca.drv->name);
+
+       ca.crlf_action = input_crlf_action(ca.crlf_action, ca.eol_attr);
+       crlf_to_git(path, dst->buf, dst->len, dst, ca.crlf_action, checksafe);
+       ident_to_git(path, dst->buf, dst->len, dst, ca.ident);
+}
+
 static int convert_to_working_tree_internal(const char *path, const char *src,
                                            size_t len, struct strbuf *dst,
                                            int normalizing)
@@ -810,7 +853,7 @@ static int convert_to_working_tree_internal(const char *path, const char *src,
                }
        }
 
-       ret_filter = apply_filter(path, src, len, dst, filter);
+       ret_filter = apply_filter(path, src, len, -1, dst, filter);
        if (!ret_filter && required)
                die("%s: smudge filter %s failed", path, ca.drv->name);
 
index 0c2143c152734feb3bdc7ba44b61b8327f02881c..d9d853cd3d2f6d94965a2c2fc8e587386402a83b 100644 (file)
--- a/convert.h
+++ b/convert.h
@@ -40,11 +40,15 @@ extern int convert_to_working_tree(const char *path, const char *src,
                                   size_t len, struct strbuf *dst);
 extern int renormalize_buffer(const char *path, const char *src, size_t len,
                              struct strbuf *dst);
-static inline int would_convert_to_git(const char *path, const char *src,
-                                      size_t len, enum safe_crlf checksafe)
+static inline int would_convert_to_git(const char *path)
 {
-       return convert_to_git(path, src, len, NULL, checksafe);
+       return convert_to_git(path, NULL, 0, NULL, 0);
 }
+/* Precondition: would_convert_to_git_filter_fd(path) == true */
+extern void convert_to_git_filter_fd(const char *path, int fd,
+                                    struct strbuf *dst,
+                                    enum safe_crlf checksafe);
+extern int would_convert_to_git_filter_fd(const char *path);
 
 /*****************************************************************
  *
diff --git a/copy.c b/copy.c
index a7f58fd905b31b2634f74580090ec664a640e279..f2970ec46282a07593366c598f688e5d70c05c83 100644 (file)
--- a/copy.c
+++ b/copy.c
@@ -4,34 +4,17 @@ int copy_fd(int ifd, int ofd)
 {
        while (1) {
                char buffer[8192];
-               char *buf = buffer;
                ssize_t len = xread(ifd, buffer, sizeof(buffer));
                if (!len)
                        break;
                if (len < 0) {
-                       int read_error = errno;
-                       close(ifd);
                        return error("copy-fd: read returned %s",
-                                    strerror(read_error));
-               }
-               while (len) {
-                       int written = xwrite(ofd, buf, len);
-                       if (written > 0) {
-                               buf += written;
-                               len -= written;
-                       }
-                       else if (!written) {
-                               close(ifd);
-                               return error("copy-fd: write returned 0");
-                       } else {
-                               int write_error = errno;
-                               close(ifd);
-                               return error("copy-fd: write returned %s",
-                                            strerror(write_error));
-                       }
+                                    strerror(errno));
                }
+               if (write_in_full(ofd, buffer, len) < 0)
+                       return error("copy-fd: write returned %s",
+                                    strerror(errno));
        }
-       close(ifd);
        return 0;
 }
 
@@ -60,6 +43,7 @@ int copy_file(const char *dst, const char *src, int mode)
                return fdo;
        }
        status = copy_fd(fdi, fdo);
+       close(fdi);
        if (close(fdo) != 0)
                return error("%s: close error: %s", dst, strerror(errno));
 
index f9146e576f82cd774f697420e2bb456f56ad65dd..d435514cbe29aa01d9cebadb947a6c6c8a1e2527 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "credential.h"
 #include "string-list.h"
 #include "parse-options.h"
index 4dcfff9352c8d034bfaec4efa0c36df41e7813a7..54a03bd527a81485b498e33225ed54c54c0dbff7 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -553,20 +553,21 @@ static void parse_host_arg(char *extra_args, int buflen)
                static char addrbuf[HOST_NAME_MAX + 1];
 
                hent = gethostbyname(hostname);
+               if (hent) {
+                       ap = hent->h_addr_list;
+                       memset(&sa, 0, sizeof sa);
+                       sa.sin_family = hent->h_addrtype;
+                       sa.sin_port = htons(0);
+                       memcpy(&sa.sin_addr, *ap, hent->h_length);
+
+                       inet_ntop(hent->h_addrtype, &sa.sin_addr,
+                                 addrbuf, sizeof(addrbuf));
 
-               ap = hent->h_addr_list;
-               memset(&sa, 0, sizeof sa);
-               sa.sin_family = hent->h_addrtype;
-               sa.sin_port = htons(0);
-               memcpy(&sa.sin_addr, *ap, hent->h_length);
-
-               inet_ntop(hent->h_addrtype, &sa.sin_addr,
-                         addrbuf, sizeof(addrbuf));
-
-               free(canon_hostname);
-               canon_hostname = xstrdup(hent->h_name);
-               free(ip_address);
-               ip_address = xstrdup(addrbuf);
+                       free(canon_hostname);
+                       canon_hostname = xstrdup(hent->h_name);
+                       free(ip_address);
+                       ip_address = xstrdup(addrbuf);
+               }
 #endif
        }
 }
@@ -814,7 +815,6 @@ static const char *ip2str(int family, struct sockaddr *sin, socklen_t len)
 static int setup_named_sock(char *listen_addr, int listen_port, struct socketlist *socklist)
 {
        int socknum = 0;
-       int maxfd = -1;
        char pbuf[NI_MAXSERV];
        struct addrinfo hints, *ai0, *ai;
        int gai;
@@ -882,9 +882,6 @@ static int setup_named_sock(char *listen_addr, int listen_port, struct socketlis
                ALLOC_GROW(socklist->list, socklist->nr + 1, socklist->alloc);
                socklist->list[socklist->nr++] = sockfd;
                socknum++;
-
-               if (maxfd < sockfd)
-                       maxfd = sockfd;
        }
 
        freeaddrinfo(ai0);
@@ -923,7 +920,7 @@ static int setup_named_sock(char *listen_addr, int listen_port, struct socketlis
        }
 
        if ( bind(sockfd, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
-               logerror("Could not listen to %s: %s",
+               logerror("Could not bind to %s: %s",
                         ip2str(AF_INET, (struct sockaddr *)&sin, sizeof(sin)),
                         strerror(errno));
                close(sockfd);
index 96b0f4236a2083044e4ae1dfa3b8180e3e586ba1..fee7906e51792f4bfbfab0b1db2dec3082025edb 100644 (file)
@@ -153,6 +153,7 @@ Format of STDIN stream:
 
 #include "builtin.h"
 #include "cache.h"
+#include "lockfile.h"
 #include "object.h"
 #include "blob.h"
 #include "tree.h"
@@ -1793,20 +1794,18 @@ static void dump_marks_helper(FILE *f,
 static void dump_marks(void)
 {
        static struct lock_file mark_lock;
-       int mark_fd;
        FILE *f;
 
        if (!export_marks_file)
                return;
 
-       mark_fd = hold_lock_file_for_update(&mark_lock, export_marks_file, 0);
-       if (mark_fd < 0) {
+       if (hold_lock_file_for_update(&mark_lock, export_marks_file, 0) < 0) {
                failure |= error("Unable to write marks file %s: %s",
                        export_marks_file, strerror(errno));
                return;
        }
 
-       f = fdopen(mark_fd, "w");
+       f = fdopen_lock_file(&mark_lock, "w");
        if (!f) {
                int saved_errno = errno;
                rollback_lock_file(&mark_lock);
@@ -1815,27 +1814,10 @@ static void dump_marks(void)
                return;
        }
 
-       /*
-        * Since the lock file was fdopen()'ed, it should not be close()'ed.
-        * Assign -1 to the lock file descriptor so that commit_lock_file()
-        * won't try to close() it.
-        */
-       mark_lock.fd = -1;
-
        dump_marks_helper(f, 0, marks);
-       if (ferror(f) || fclose(f)) {
-               int saved_errno = errno;
-               rollback_lock_file(&mark_lock);
-               failure |= error("Unable to write marks file %s: %s",
-                       export_marks_file, strerror(saved_errno));
-               return;
-       }
-
        if (commit_lock_file(&mark_lock)) {
-               int saved_errno = errno;
-               rollback_lock_file(&mark_lock);
                failure |= error("Unable to commit marks file %s: %s",
-                       export_marks_file, strerror(saved_errno));
+                       export_marks_file, strerror(errno));
                return;
        }
 }
index 7487aa730630e8b45874e3487db8e71c05e1968c..655ee642564e3ada5b782dd6fa2d2c799f340978 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "refs.h"
 #include "pkt-line.h"
 #include "commit.h"
index 1ef73fb7dfedd8cd5d3444433218ff177a63111b..68b0c814f789f39151d380b1d318d6f80090d524 100644 (file)
@@ -7,6 +7,9 @@
 static char *configured_signing_key;
 static const char *gpg_program = "gpg";
 
+#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
+#define PGP_MESSAGE "-----BEGIN PGP MESSAGE-----"
+
 void signature_check_clear(struct signature_check *sigc)
 {
        free(sigc->payload);
@@ -21,6 +24,60 @@ void signature_check_clear(struct signature_check *sigc)
        sigc->key = NULL;
 }
 
+static struct {
+       char result;
+       const char *check;
+} sigcheck_gpg_status[] = {
+       { 'G', "\n[GNUPG:] GOODSIG " },
+       { 'B', "\n[GNUPG:] BADSIG " },
+       { 'U', "\n[GNUPG:] TRUST_NEVER" },
+       { 'U', "\n[GNUPG:] TRUST_UNDEFINED" },
+};
+
+void parse_gpg_output(struct signature_check *sigc)
+{
+       const char *buf = sigc->gpg_status;
+       int i;
+
+       /* Iterate over all search strings */
+       for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
+               const char *found, *next;
+
+               if (!skip_prefix(buf, sigcheck_gpg_status[i].check + 1, &found)) {
+                       found = strstr(buf, sigcheck_gpg_status[i].check);
+                       if (!found)
+                               continue;
+                       found += strlen(sigcheck_gpg_status[i].check);
+               }
+               sigc->result = sigcheck_gpg_status[i].result;
+               /* The trust messages are not followed by key/signer information */
+               if (sigc->result != 'U') {
+                       sigc->key = xmemdupz(found, 16);
+                       found += 17;
+                       next = strchrnul(found, '\n');
+                       sigc->signer = xmemdupz(found, next - found);
+               }
+       }
+}
+
+/*
+ * Look at GPG signed content (e.g. a signed tag object), whose
+ * payload is followed by a detached signature on it.  Return the
+ * offset where the embedded detached signature begins, or the end of
+ * the data when there is no such signature.
+ */
+size_t parse_signature(const char *buf, unsigned long size)
+{
+       char *eol;
+       size_t len = 0;
+       while (len < size && !starts_with(buf + len, PGP_SIGNATURE) &&
+                       !starts_with(buf + len, PGP_MESSAGE)) {
+               eol = memchr(buf + len, '\n', size - len);
+               len += eol ? eol - (buf + len) + 1 : size - len;
+       }
+       return len;
+}
+
 void set_signing_key(const char *key)
 {
        free(configured_signing_key);
index 37c23daff010b0de18fa12ff6a6167f45ff41ffc..87a4f2e3fad92b4622a40ef5612db293bf4c5dd4 100644 (file)
@@ -5,16 +5,23 @@ struct signature_check {
        char *payload;
        char *gpg_output;
        char *gpg_status;
-       char result; /* 0 (not checked),
-                     * N (checked but no further result),
-                     * U (untrusted good),
-                     * G (good)
-                     * B (bad) */
+
+       /*
+        * possible "result":
+        * 0 (not checked)
+        * N (checked but no further result)
+        * U (untrusted good)
+        * G (good)
+        * B (bad)
+        */
+       char result;
        char *signer;
        char *key;
 };
 
 extern void signature_check_clear(struct signature_check *sigc);
+extern size_t parse_signature(const char *buf, unsigned long size);
+extern void parse_gpg_output(struct signature_check *);
 extern int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *signing_key);
 extern int verify_signed_buffer(const char *payload, size_t payload_size, const char *signature, size_t signature_size, struct strbuf *gpg_output, struct strbuf *gpg_status);
 extern int git_gpg_config(const char *, const char *, void *);
diff --git a/http.c b/http.c
index 0adcec4683f997044807a772713d476dd77cf105..040f362a6a299618288c9249588ceb7aed6f3011 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1,3 +1,4 @@
+#include "git-compat-util.h"
 #include "http.h"
 #include "pack.h"
 #include "sideband.h"
index 2a800cef33ed7fb1ba7d3499459eccd824ab33e4..d098adebf1454ea9c9204c9dd2fda4d51b63a1e5 100644 (file)
@@ -2,59 +2,61 @@
  * Copyright (c) 2005, Junio C Hamano
  */
 #include "cache.h"
+#include "lockfile.h"
 #include "sigchain.h"
 
-static struct lock_file *lock_file_list;
+static struct lock_file *volatile lock_file_list;
 
-static void remove_lock_file(void)
+static void remove_lock_files(int skip_fclose)
 {
        pid_t me = getpid();
 
        while (lock_file_list) {
-               if (lock_file_list->owner == me &&
-                   lock_file_list->filename[0]) {
-                       if (lock_file_list->fd >= 0)
-                               close(lock_file_list->fd);
-                       unlink_or_warn(lock_file_list->filename);
+               if (lock_file_list->owner == me) {
+                       /* fclose() is not safe to call in a signal handler */
+                       if (skip_fclose)
+                               lock_file_list->fp = NULL;
+                       rollback_lock_file(lock_file_list);
                }
                lock_file_list = lock_file_list->next;
        }
 }
 
-static void remove_lock_file_on_signal(int signo)
+static void remove_lock_files_on_exit(void)
 {
-       remove_lock_file();
+       remove_lock_files(0);
+}
+
+static void remove_lock_files_on_signal(int signo)
+{
+       remove_lock_files(1);
        sigchain_pop(signo);
        raise(signo);
 }
 
 /*
- * p = absolute or relative path name
+ * path = absolute or relative path name
  *
- * Return a pointer into p showing the beginning of the last path name
- * element.  If p is empty or the root directory ("/"), just return p.
+ * Remove the last path name element from path (leaving the preceding
+ * "/", if any).  If path is empty or the root directory ("/"), set
+ * path to the empty string.
  */
-static char *last_path_elm(char *p)
+static void trim_last_path_component(struct strbuf *path)
 {
-       /* r starts pointing to null at the end of the string */
-       char *r = strchr(p, '\0');
-
-       if (r == p)
-               return p; /* just return empty string */
-
-       r--; /* back up to last non-null character */
+       int i = path->len;
 
        /* back up past trailing slashes, if any */
-       while (r > p && *r == '/')
-               r--;
+       while (i && path->buf[i - 1] == '/')
+               i--;
 
        /*
-        * then go backwards until I hit a slash, or the beginning of
-        * the string
+        * then go backwards until a slash, or the beginning of the
+        * string
         */
-       while (r > p && *(r-1) != '/')
-               r--;
-       return r;
+       while (i && path->buf[i - 1] != '/')
+               i--;
+
+       strbuf_setlen(path, i);
 }
 
 
@@ -62,103 +64,88 @@ static char *last_path_elm(char *p)
 #define MAXDEPTH 5
 
 /*
- * p = path that may be a symlink
- * s = full size of p
- *
- * If p is a symlink, attempt to overwrite p with a path to the real
- * file or directory (which may or may not exist), following a chain of
- * symlinks if necessary.  Otherwise, leave p unmodified.
+ * path contains a path that might be a symlink.
  *
- * This is a best-effort routine.  If an error occurs, p will either be
- * left unmodified or will name a different symlink in a symlink chain
- * that started with p's initial contents.
+ * If path is a symlink, attempt to overwrite it with a path to the
+ * real file or directory (which may or may not exist), following a
+ * chain of symlinks if necessary.  Otherwise, leave path unmodified.
  *
- * Always returns p.
+ * This is a best-effort routine.  If an error occurs, path will
+ * either be left unmodified or will name a different symlink in a
+ * symlink chain that started with the original path.
  */
-
-static char *resolve_symlink(char *p, size_t s)
+static void resolve_symlink(struct strbuf *path)
 {
        int depth = MAXDEPTH;
+       static struct strbuf link = STRBUF_INIT;
 
        while (depth--) {
-               char link[PATH_MAX];
-               int link_len = readlink(p, link, sizeof(link));
-               if (link_len < 0) {
-                       /* not a symlink anymore */
-                       return p;
-               }
-               else if (link_len < sizeof(link))
-                       /* readlink() never null-terminates */
-                       link[link_len] = '\0';
-               else {
-                       warning("%s: symlink too long", p);
-                       return p;
-               }
+               if (strbuf_readlink(&link, path->buf, path->len) < 0)
+                       break;
 
-               if (is_absolute_path(link)) {
+               if (is_absolute_path(link.buf))
                        /* absolute path simply replaces p */
-                       if (link_len < s)
-                               strcpy(p, link);
-                       else {
-                               warning("%s: symlink too long", p);
-                               return p;
-                       }
-               } else {
+                       strbuf_reset(path);
+               else
                        /*
-                        * link is a relative path, so I must replace the
+                        * link is a relative path, so replace the
                         * last element of p with it.
                         */
-                       char *r = (char *)last_path_elm(p);
-                       if (r - p + link_len < s)
-                               strcpy(r, link);
-                       else {
-                               warning("%s: symlink too long", p);
-                               return p;
-                       }
-               }
+                       trim_last_path_component(path);
+
+               strbuf_addbuf(path, &link);
        }
-       return p;
+       strbuf_reset(&link);
 }
 
 /* Make sure errno contains a meaningful value on error */
 static int lock_file(struct lock_file *lk, const char *path, int flags)
 {
-       /*
-        * subtract 5 from size to make sure there's room for adding
-        * ".lock" for the lock file name
-        */
-       static const size_t max_path_len = sizeof(lk->filename) - 5;
+       size_t pathlen = strlen(path);
 
-       if (strlen(path) >= max_path_len) {
-               errno = ENAMETOOLONG;
+       if (!lock_file_list) {
+               /* One-time initialization */
+               sigchain_push_common(remove_lock_files_on_signal);
+               atexit(remove_lock_files_on_exit);
+       }
+
+       if (lk->active)
+               die("BUG: cannot lock_file(\"%s\") using active struct lock_file",
+                   path);
+       if (!lk->on_list) {
+               /* Initialize *lk and add it to lock_file_list: */
+               lk->fd = -1;
+               lk->fp = NULL;
+               lk->active = 0;
+               lk->owner = 0;
+               strbuf_init(&lk->filename, pathlen + LOCK_SUFFIX_LEN);
+               lk->next = lock_file_list;
+               lock_file_list = lk;
+               lk->on_list = 1;
+       } else if (lk->filename.len) {
+               /* This shouldn't happen, but better safe than sorry. */
+               die("BUG: lock_file(\"%s\") called with improperly-reset lock_file object",
+                   path);
+       }
+
+       strbuf_add(&lk->filename, path, pathlen);
+       if (!(flags & LOCK_NO_DEREF))
+               resolve_symlink(&lk->filename);
+       strbuf_addstr(&lk->filename, LOCK_SUFFIX);
+       lk->fd = open(lk->filename.buf, O_RDWR | O_CREAT | O_EXCL, 0666);
+       if (lk->fd < 0) {
+               strbuf_reset(&lk->filename);
                return -1;
        }
-       strcpy(lk->filename, path);
-       if (!(flags & LOCK_NODEREF))
-               resolve_symlink(lk->filename, max_path_len);
-       strcat(lk->filename, ".lock");
-       lk->fd = open(lk->filename, O_RDWR | O_CREAT | O_EXCL, 0666);
-       if (0 <= lk->fd) {
-               if (!lock_file_list) {
-                       sigchain_push_common(remove_lock_file_on_signal);
-                       atexit(remove_lock_file);
-               }
-               lk->owner = getpid();
-               if (!lk->on_list) {
-                       lk->next = lock_file_list;
-                       lock_file_list = lk;
-                       lk->on_list = 1;
-               }
-               if (adjust_shared_perm(lk->filename)) {
-                       int save_errno = errno;
-                       error("cannot fix permission bits on %s",
-                             lk->filename);
-                       errno = save_errno;
-                       return -1;
-               }
+       lk->owner = getpid();
+       lk->active = 1;
+       if (adjust_shared_perm(lk->filename.buf)) {
+               int save_errno = errno;
+               error("cannot fix permission bits on %s", lk->filename.buf);
+               rollback_lock_file(lk);
+               errno = save_errno;
+               return -1;
        }
-       else
-               lk->filename[0] = 0;
        return lk->fd;
 }
 
@@ -185,7 +172,7 @@ int unable_to_lock_error(const char *path, int err)
        return -1;
 }
 
-NORETURN void unable_to_lock_index_die(const char *path, int err)
+NORETURN void unable_to_lock_die(const char *path, int err)
 {
        struct strbuf buf = STRBUF_INIT;
 
@@ -198,7 +185,7 @@ int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
 {
        int fd = lock_file(lk, path, flags);
        if (fd < 0 && (flags & LOCK_DIE_ON_ERROR))
-               unable_to_lock_index_die(path, errno);
+               unable_to_lock_die(path, errno);
        return fd;
 }
 
@@ -209,73 +196,147 @@ int hold_lock_file_for_append(struct lock_file *lk, const char *path, int flags)
        fd = lock_file(lk, path, flags);
        if (fd < 0) {
                if (flags & LOCK_DIE_ON_ERROR)
-                       unable_to_lock_index_die(path, errno);
+                       unable_to_lock_die(path, errno);
                return fd;
        }
 
        orig_fd = open(path, O_RDONLY);
        if (orig_fd < 0) {
                if (errno != ENOENT) {
+                       int save_errno = errno;
+
                        if (flags & LOCK_DIE_ON_ERROR)
                                die("cannot open '%s' for copying", path);
-                       close(fd);
-                       return error("cannot open '%s' for copying", path);
+                       rollback_lock_file(lk);
+                       error("cannot open '%s' for copying", path);
+                       errno = save_errno;
+                       return -1;
                }
        } else if (copy_fd(orig_fd, fd)) {
+               int save_errno = errno;
+
                if (flags & LOCK_DIE_ON_ERROR)
                        exit(128);
-               close(fd);
+               close(orig_fd);
+               rollback_lock_file(lk);
+               errno = save_errno;
                return -1;
+       } else {
+               close(orig_fd);
        }
        return fd;
 }
 
+FILE *fdopen_lock_file(struct lock_file *lk, const char *mode)
+{
+       if (!lk->active)
+               die("BUG: fdopen_lock_file() called for unlocked object");
+       if (lk->fp)
+               die("BUG: fdopen_lock_file() called twice for file '%s'", lk->filename.buf);
+
+       lk->fp = fdopen(lk->fd, mode);
+       return lk->fp;
+}
+
+char *get_locked_file_path(struct lock_file *lk)
+{
+       if (!lk->active)
+               die("BUG: get_locked_file_path() called for unlocked object");
+       if (lk->filename.len <= LOCK_SUFFIX_LEN)
+               die("BUG: get_locked_file_path() called for malformed lock object");
+       return xmemdupz(lk->filename.buf, lk->filename.len - LOCK_SUFFIX_LEN);
+}
+
 int close_lock_file(struct lock_file *lk)
 {
        int fd = lk->fd;
+       FILE *fp = lk->fp;
+       int err;
+
+       if (fd < 0)
+               return 0;
+
        lk->fd = -1;
-       return close(fd);
+       if (fp) {
+               lk->fp = NULL;
+
+               /*
+                * Note: no short-circuiting here; we want to fclose()
+                * in any case!
+                */
+               err = ferror(fp) | fclose(fp);
+       } else {
+               err = close(fd);
+       }
+
+       if (err) {
+               int save_errno = errno;
+               rollback_lock_file(lk);
+               errno = save_errno;
+               return -1;
+       }
+
+       return 0;
 }
 
 int reopen_lock_file(struct lock_file *lk)
 {
        if (0 <= lk->fd)
                die(_("BUG: reopen a lockfile that is still open"));
-       if (!lk->filename[0])
+       if (!lk->active)
                die(_("BUG: reopen a lockfile that has been committed"));
-       lk->fd = open(lk->filename, O_WRONLY);
+       lk->fd = open(lk->filename.buf, O_WRONLY);
        return lk->fd;
 }
 
-int commit_lock_file(struct lock_file *lk)
+int commit_lock_file_to(struct lock_file *lk, const char *path)
 {
-       char result_file[PATH_MAX];
-       size_t i;
-       if (lk->fd >= 0 && close_lock_file(lk))
+       if (!lk->active)
+               die("BUG: attempt to commit unlocked object to \"%s\"", path);
+
+       if (close_lock_file(lk))
                return -1;
-       strcpy(result_file, lk->filename);
-       i = strlen(result_file) - 5; /* .lock */
-       result_file[i] = 0;
-       if (rename(lk->filename, result_file))
+
+       if (rename(lk->filename.buf, path)) {
+               int save_errno = errno;
+               rollback_lock_file(lk);
+               errno = save_errno;
                return -1;
-       lk->filename[0] = 0;
+       }
+
+       lk->active = 0;
+       strbuf_reset(&lk->filename);
        return 0;
 }
 
-int hold_locked_index(struct lock_file *lk, int die_on_error)
+int commit_lock_file(struct lock_file *lk)
 {
-       return hold_lock_file_for_update(lk, get_index_file(),
-                                        die_on_error
-                                        ? LOCK_DIE_ON_ERROR
-                                        : 0);
+       static struct strbuf result_file = STRBUF_INIT;
+       int err;
+
+       if (!lk->active)
+               die("BUG: attempt to commit unlocked object");
+
+       if (lk->filename.len <= LOCK_SUFFIX_LEN ||
+           strcmp(lk->filename.buf + lk->filename.len - LOCK_SUFFIX_LEN, LOCK_SUFFIX))
+               die("BUG: lockfile filename corrupt");
+
+       /* remove ".lock": */
+       strbuf_add(&result_file, lk->filename.buf,
+                  lk->filename.len - LOCK_SUFFIX_LEN);
+       err = commit_lock_file_to(lk, result_file.buf);
+       strbuf_reset(&result_file);
+       return err;
 }
 
 void rollback_lock_file(struct lock_file *lk)
 {
-       if (lk->filename[0]) {
-               if (lk->fd >= 0)
-                       close(lk->fd);
-               unlink_or_warn(lk->filename);
+       if (!lk->active)
+               return;
+
+       if (!close_lock_file(lk)) {
+               unlink_or_warn(lk->filename.buf);
+               lk->active = 0;
+               strbuf_reset(&lk->filename);
        }
-       lk->filename[0] = 0;
 }
diff --git a/lockfile.h b/lockfile.h
new file mode 100644 (file)
index 0000000..dc066d1
--- /dev/null
@@ -0,0 +1,88 @@
+#ifndef LOCKFILE_H
+#define LOCKFILE_H
+
+/*
+ * File write-locks as used by Git.
+ *
+ * For an overview of how to use the lockfile API, please see
+ *
+ *     Documentation/technical/api-lockfile.txt
+ *
+ * This module keeps track of all locked files in lock_file_list for
+ * use at cleanup. This list and the lock_file objects that comprise
+ * it must be kept in self-consistent states at all time, because the
+ * program can be interrupted any time by a signal, in which case the
+ * signal handler will walk through the list attempting to clean up
+ * any open lock files.
+ *
+ * A lockfile is owned by the process that created it. The lock_file
+ * object has an "owner" field that records its owner. This field is
+ * used to prevent a forked process from closing a lockfile created by
+ * its parent.
+ *
+ * The possible states of a lock_file object are as follows:
+ *
+ * - Uninitialized.  In this state the object's on_list field must be
+ *   zero but the rest of its contents need not be initialized.  As
+ *   soon as the object is used in any way, it is irrevocably
+ *   registered in the lock_file_list, and on_list is set.
+ *
+ * - Locked, lockfile open (after hold_lock_file_for_update(),
+ *   hold_lock_file_for_append(), or reopen_lock_file()). In this
+ *   state:
+ *   - the lockfile exists
+ *   - active is set
+ *   - filename holds the filename of the lockfile
+ *   - fd holds a file descriptor open for writing to the lockfile
+ *   - fp holds a pointer to an open FILE object if and only if
+ *     fdopen_lock_file() has been called on the object
+ *   - owner holds the PID of the process that locked the file
+ *
+ * - Locked, lockfile closed (after successful close_lock_file()).
+ *   Same as the previous state, except that the lockfile is closed
+ *   and fd is -1.
+ *
+ * - Unlocked (after commit_lock_file(), commit_lock_file_to(),
+ *   rollback_lock_file(), a failed attempt to lock, or a failed
+ *   close_lock_file()).  In this state:
+ *   - active is unset
+ *   - filename is empty (usually, though there are transitory
+ *     states in which this condition doesn't hold). Client code should
+ *     *not* rely on the filename being empty in this state.
+ *   - fd is -1
+ *   - the object is left registered in the lock_file_list, and
+ *     on_list is set.
+ */
+
+struct lock_file {
+       struct lock_file *volatile next;
+       volatile sig_atomic_t active;
+       volatile int fd;
+       FILE *volatile fp;
+       volatile pid_t owner;
+       char on_list;
+       struct strbuf filename;
+};
+
+/* String appended to a filename to derive the lockfile name: */
+#define LOCK_SUFFIX ".lock"
+#define LOCK_SUFFIX_LEN 5
+
+#define LOCK_DIE_ON_ERROR 1
+#define LOCK_NO_DEREF 2
+
+extern int unable_to_lock_error(const char *path, int err);
+extern void unable_to_lock_message(const char *path, int err,
+                                  struct strbuf *buf);
+extern NORETURN void unable_to_lock_die(const char *path, int err);
+extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
+extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
+extern FILE *fdopen_lock_file(struct lock_file *, const char *mode);
+extern char *get_locked_file_path(struct lock_file *);
+extern int commit_lock_file_to(struct lock_file *, const char *path);
+extern int commit_lock_file(struct lock_file *);
+extern int reopen_lock_file(struct lock_file *);
+extern int close_lock_file(struct lock_file *);
+extern void rollback_lock_file(struct lock_file *);
+
+#endif /* LOCKFILE_H */
index 8ad4be897d88c691d43c30afb698a1ae952b8596..fdb7d0f10ba471bcdff26dc87851d7df34fdc971 100644 (file)
@@ -3,8 +3,9 @@
  * Fredrik Kuivinen.
  * The thieves were Alex Riesen and Johannes Schindelin, in June/July 2006
  */
-#include "advice.h"
 #include "cache.h"
+#include "advice.h"
+#include "lockfile.h"
 #include "cache-tree.h"
 #include "commit.h"
 #include "blob.h"
diff --git a/merge.c b/merge.c
index 74ced7f70b5beec045c134b5719c926fc791f1e2..fcff632bd63a113ca78b0bd86da1e6af8bc6f7dc 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "commit.h"
 #include "run-command.h"
 #include "resolve-undo.h"
index 2fc1182f2267b3e9aed799eaf7df234cd36fefb9..8f3e9eb31498d708acf63bf3fc6de3d34562d42e 100644 (file)
@@ -5,6 +5,7 @@
  */
 #define NO_THE_INDEX_COMPATIBILITY_MACROS
 #include "cache.h"
+#include "lockfile.h"
 #include "cache-tree.h"
 #include "refs.h"
 #include "dir.h"
@@ -1367,6 +1368,14 @@ static int read_index_extension(struct index_state *istate,
        return 0;
 }
 
+int hold_locked_index(struct lock_file *lk, int die_on_error)
+{
+       return hold_lock_file_for_update(lk, get_index_file(),
+                                        die_on_error
+                                        ? LOCK_DIE_ON_ERROR
+                                        : 0);
+}
+
 int read_index(struct index_state *istate)
 {
        return read_index_from(istate, get_index_file());
@@ -2041,16 +2050,10 @@ void set_alternate_index_output(const char *name)
 
 static int commit_locked_index(struct lock_file *lk)
 {
-       if (alternate_index_output) {
-               if (lk->fd >= 0 && close_lock_file(lk))
-                       return -1;
-               if (rename(lk->filename, alternate_index_output))
-                       return -1;
-               lk->filename[0] = 0;
-               return 0;
-       } else {
+       if (alternate_index_output)
+               return commit_lock_file_to(lk, alternate_index_output);
+       else
                return commit_lock_file(lk);
-       }
 }
 
 static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
diff --git a/refs.c b/refs.c
index ffd45e92922ec52dccccfaecf1ceaf29f9231337..a77458f2f6eb108a90a1cf3d2cc4a771549c05f9 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "refs.h"
 #include "object.h"
 #include "tag.h"
@@ -79,7 +80,8 @@ static int check_refname_component(const char *refname, int flags)
                if (refname[1] == '\0')
                        return -1; /* Component equals ".". */
        }
-       if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5))
+       if (cp - refname >= LOCK_SUFFIX_LEN &&
+           !memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN))
                return -1; /* Refname ends with ".lock". */
        return cp - refname;
 }
@@ -2191,7 +2193,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
        lflags = 0;
        if (flags & REF_NODEREF) {
                refname = orig_refname;
-               lflags |= LOCK_NODEREF;
+               lflags |= LOCK_NO_DEREF;
        }
        lock->ref_name = xstrdup(refname);
        lock->orig_ref_name = xstrdup(orig_refname);
@@ -2225,7 +2227,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                         */
                        goto retry;
                else
-                       unable_to_lock_index_die(ref_file, errno);
+                       unable_to_lock_die(ref_file, errno);
        }
        return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
 
@@ -2307,16 +2309,13 @@ int commit_packed_refs(void)
        if (!packed_ref_cache->lock)
                die("internal error: packed-refs not locked");
 
-       out = fdopen(packed_ref_cache->lock->fd, "w");
+       out = fdopen_lock_file(packed_ref_cache->lock, "w");
        if (!out)
                die_errno("unable to fdopen packed-refs descriptor");
 
        fprintf_or_die(out, "%s", PACKED_REFS_HEADER);
        do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
                                 0, write_packed_entry_fn, out);
-       if (fclose(out))
-               die_errno("write error");
-       packed_ref_cache->lock->fd = -1;
 
        if (commit_lock_file(packed_ref_cache->lock)) {
                save_errno = errno;
@@ -2601,12 +2600,13 @@ int repack_without_refs(const char **refnames, int n, struct strbuf *err)
 static int delete_ref_loose(struct ref_lock *lock, int flag)
 {
        if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
-               /* loose */
-               int err, i = strlen(lock->lk->filename) - 5; /* .lock */
-
-               lock->lk->filename[i] = 0;
-               err = unlink_or_warn(lock->lk->filename);
-               lock->lk->filename[i] = '.';
+               /*
+                * loose.  The loose file name is the same as the
+                * lockfile name, minus ".lock":
+                */
+               char *loose_filename = get_locked_file_path(lock->lk);
+               int err = unlink_or_warn(loose_filename);
+               free(loose_filename);
                if (err && errno != ENOENT)
                        return 1;
        }
@@ -2968,7 +2968,7 @@ int write_ref_sha1(struct ref_lock *lock,
            write_in_full(lock->lock_fd, &term, 1) != 1 ||
            close_ref(lock) < 0) {
                int save_errno = errno;
-               error("Couldn't write %s", lock->lk->filename);
+               error("Couldn't write %s", lock->lk->filename.buf);
                unlock_ref(lock);
                errno = save_errno;
                return -1;
index cd626d15e59832e6fee74b2ca845a23a5fce77ee..dd63bc27abf8ae4b9e480ba1bb88942db11a0e48 100644 (file)
@@ -25,7 +25,8 @@ struct options {
                update_shallow : 1,
                followtags : 1,
                dry_run : 1,
-               thin : 1;
+               thin : 1,
+               push_cert : 1;
 };
 static struct options options;
 static struct string_list cas_options = STRING_LIST_INIT_DUP;
@@ -106,6 +107,14 @@ static int set_option(const char *name, const char *value)
                else
                        return -1;
                return 0;
+       } else if (!strcmp(name, "pushcert")) {
+               if (!strcmp(value, "true"))
+                       options.push_cert = 1;
+               else if (!strcmp(value, "false"))
+                       options.push_cert = 0;
+               else
+                       return -1;
+               return 0;
        } else {
                return 1 /* unsupported */;
        }
@@ -872,6 +881,8 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
                argv_array_push(&args, "--thin");
        if (options.dry_run)
                argv_array_push(&args, "--dry-run");
+       if (options.push_cert)
+               argv_array_push(&args, "--signed");
        if (options.verbosity == 0)
                argv_array_push(&args, "--quiet");
        else if (options.verbosity > 1)
index 20b18add42d2551a227eaac8125fe806006d2856..1b0555f1a5a0233e931b13a088c052a56aef3977 100644 (file)
--- a/rerere.c
+++ b/rerere.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "string-list.h"
 #include "rerere.h"
 #include "xdiff-interface.h"
index 8b4cbf049c243b8cdc1add94ddf1bf50bcbd8df9..949cb61aa0d681e30b7e8199ef9e31ff0caee089 100644 (file)
@@ -11,6 +11,7 @@
 #include "transport.h"
 #include "version.h"
 #include "sha1-array.h"
+#include "gpg-interface.h"
 
 static int feed_object(const unsigned char *sha1, int fd, int negative)
 {
@@ -189,6 +190,94 @@ static void advertise_shallow_grafts_buf(struct strbuf *sb)
        for_each_commit_graft(advertise_shallow_grafts_cb, sb);
 }
 
+static int ref_update_to_be_sent(const struct ref *ref, const struct send_pack_args *args)
+{
+       if (!ref->peer_ref && !args->send_mirror)
+               return 0;
+
+       /* Check for statuses set by set_ref_status_for_push() */
+       switch (ref->status) {
+       case REF_STATUS_REJECT_NONFASTFORWARD:
+       case REF_STATUS_REJECT_ALREADY_EXISTS:
+       case REF_STATUS_REJECT_FETCH_FIRST:
+       case REF_STATUS_REJECT_NEEDS_FORCE:
+       case REF_STATUS_REJECT_STALE:
+       case REF_STATUS_REJECT_NODELETE:
+       case REF_STATUS_UPTODATE:
+               return 0;
+       default:
+               return 1;
+       }
+}
+
+/*
+ * the beginning of the next line, or the end of buffer.
+ *
+ * NEEDSWORK: perhaps move this to git-compat-util.h or somewhere and
+ * convert many similar uses found by "git grep -A4 memchr".
+ */
+static const char *next_line(const char *line, size_t len)
+{
+       const char *nl = memchr(line, '\n', len);
+       if (!nl)
+               return line + len; /* incomplete line */
+       return nl + 1;
+}
+
+static int generate_push_cert(struct strbuf *req_buf,
+                             const struct ref *remote_refs,
+                             struct send_pack_args *args,
+                             const char *cap_string,
+                             const char *push_cert_nonce)
+{
+       const struct ref *ref;
+       char *signing_key = xstrdup(get_signing_key());
+       const char *cp, *np;
+       struct strbuf cert = STRBUF_INIT;
+       int update_seen = 0;
+
+       strbuf_addf(&cert, "certificate version 0.1\n");
+       strbuf_addf(&cert, "pusher %s ", signing_key);
+       datestamp(&cert);
+       strbuf_addch(&cert, '\n');
+       if (args->url && *args->url) {
+               char *anon_url = transport_anonymize_url(args->url);
+               strbuf_addf(&cert, "pushee %s\n", anon_url);
+               free(anon_url);
+       }
+       if (push_cert_nonce[0])
+               strbuf_addf(&cert, "nonce %s\n", push_cert_nonce);
+       strbuf_addstr(&cert, "\n");
+
+       for (ref = remote_refs; ref; ref = ref->next) {
+               if (!ref_update_to_be_sent(ref, args))
+                       continue;
+               update_seen = 1;
+               strbuf_addf(&cert, "%s %s %s\n",
+                           sha1_to_hex(ref->old_sha1),
+                           sha1_to_hex(ref->new_sha1),
+                           ref->name);
+       }
+       if (!update_seen)
+               goto free_return;
+
+       if (sign_buffer(&cert, &cert, signing_key))
+               die(_("failed to sign the push certificate"));
+
+       packet_buf_write(req_buf, "push-cert%c%s", 0, cap_string);
+       for (cp = cert.buf; cp < cert.buf + cert.len; cp = np) {
+               np = next_line(cp, cert.buf + cert.len - cp);
+               packet_buf_write(req_buf,
+                                "%.*s", (int)(np - cp), cp);
+       }
+       packet_buf_write(req_buf, "push-cert-end\n");
+
+free_return:
+       free(signing_key);
+       strbuf_release(&cert);
+       return update_seen;
+}
+
 int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
              struct ref *remote_refs,
@@ -197,8 +286,9 @@ int send_pack(struct send_pack_args *args,
        int in = fd[0];
        int out = fd[1];
        struct strbuf req_buf = STRBUF_INIT;
+       struct strbuf cap_buf = STRBUF_INIT;
        struct ref *ref;
-       int new_refs;
+       int need_pack_data = 0;
        int allow_deleting_refs = 0;
        int status_report = 0;
        int use_sideband = 0;
@@ -207,6 +297,7 @@ int send_pack(struct send_pack_args *args,
        unsigned cmds_sent = 0;
        int ret;
        struct async demux;
+       const char *push_cert_nonce = NULL;
 
        /* Does the other end support the reporting? */
        if (server_supports("report-status"))
@@ -223,6 +314,14 @@ int send_pack(struct send_pack_args *args,
                agent_supported = 1;
        if (server_supports("no-thin"))
                args->use_thin_pack = 0;
+       if (args->push_cert) {
+               int len;
+
+               push_cert_nonce = server_feature_value("push-cert", &len);
+               if (!push_cert_nonce)
+                       die(_("the receiving end does not support --signed push"));
+               push_cert_nonce = xmemdupz(push_cert_nonce, len);
+       }
 
        if (!remote_refs) {
                fprintf(stderr, "No refs in common and none specified; doing nothing.\n"
@@ -230,64 +329,71 @@ int send_pack(struct send_pack_args *args,
                return 0;
        }
 
+       if (status_report)
+               strbuf_addstr(&cap_buf, " report-status");
+       if (use_sideband)
+               strbuf_addstr(&cap_buf, " side-band-64k");
+       if (quiet_supported && (args->quiet || !args->progress))
+               strbuf_addstr(&cap_buf, " quiet");
+       if (agent_supported)
+               strbuf_addf(&cap_buf, " agent=%s", git_user_agent_sanitized());
+
+       /*
+        * NEEDSWORK: why does delete-refs have to be so specific to
+        * send-pack machinery that set_ref_status_for_push() cannot
+        * set this bit for us???
+        */
+       for (ref = remote_refs; ref; ref = ref->next)
+               if (ref->deletion && !allow_deleting_refs)
+                       ref->status = REF_STATUS_REJECT_NODELETE;
+
        if (!args->dry_run)
                advertise_shallow_grafts_buf(&req_buf);
 
+       if (!args->dry_run && args->push_cert)
+               cmds_sent = generate_push_cert(&req_buf, remote_refs, args,
+                                              cap_buf.buf, push_cert_nonce);
+
        /*
-        * Finally, tell the other end!
+        * Clear the status for each ref and see if we need to send
+        * the pack data.
         */
-       new_refs = 0;
        for (ref = remote_refs; ref; ref = ref->next) {
-               if (!ref->peer_ref && !args->send_mirror)
+               if (!ref_update_to_be_sent(ref, args))
                        continue;
 
-               /* Check for statuses set by set_ref_status_for_push() */
-               switch (ref->status) {
-               case REF_STATUS_REJECT_NONFASTFORWARD:
-               case REF_STATUS_REJECT_ALREADY_EXISTS:
-               case REF_STATUS_REJECT_FETCH_FIRST:
-               case REF_STATUS_REJECT_NEEDS_FORCE:
-               case REF_STATUS_REJECT_STALE:
-               case REF_STATUS_UPTODATE:
-                       continue;
-               default:
-                       ; /* do nothing */
-               }
+               if (!ref->deletion)
+                       need_pack_data = 1;
 
-               if (ref->deletion && !allow_deleting_refs) {
-                       ref->status = REF_STATUS_REJECT_NODELETE;
+               if (args->dry_run || !status_report)
+                       ref->status = REF_STATUS_OK;
+               else
+                       ref->status = REF_STATUS_EXPECTING_REPORT;
+       }
+
+       /*
+        * Finally, tell the other end!
+        */
+       for (ref = remote_refs; ref; ref = ref->next) {
+               char *old_hex, *new_hex;
+
+               if (args->dry_run || args->push_cert)
                        continue;
-               }
 
-               if (!ref->deletion)
-                       new_refs++;
+               if (!ref_update_to_be_sent(ref, args))
+                       continue;
 
-               if (args->dry_run) {
-                       ref->status = REF_STATUS_OK;
+               old_hex = sha1_to_hex(ref->old_sha1);
+               new_hex = sha1_to_hex(ref->new_sha1);
+               if (!cmds_sent) {
+                       packet_buf_write(&req_buf,
+                                        "%s %s %s%c%s",
+                                        old_hex, new_hex, ref->name, 0,
+                                        cap_buf.buf);
+                       cmds_sent = 1;
                } else {
-                       char *old_hex = sha1_to_hex(ref->old_sha1);
-                       char *new_hex = sha1_to_hex(ref->new_sha1);
-                       int quiet = quiet_supported && (args->quiet || !args->progress);
-
-                       if (!cmds_sent && (status_report || use_sideband ||
-                                          quiet || agent_supported)) {
-                               packet_buf_write(&req_buf,
-                                                "%s %s %s%c%s%s%s%s%s",
-                                                old_hex, new_hex, ref->name, 0,
-                                                status_report ? " report-status" : "",
-                                                use_sideband ? " side-band-64k" : "",
-                                                quiet ? " quiet" : "",
-                                                agent_supported ? " agent=" : "",
-                                                agent_supported ? git_user_agent_sanitized() : ""
-                                               );
-                       }
-                       else
-                               packet_buf_write(&req_buf, "%s %s %s",
-                                                old_hex, new_hex, ref->name);
-                       ref->status = status_report ?
-                               REF_STATUS_EXPECTING_REPORT :
-                               REF_STATUS_OK;
-                       cmds_sent++;
+                       packet_buf_write(&req_buf, "%s %s %s",
+                                        old_hex, new_hex, ref->name);
                }
        }
 
@@ -301,6 +407,7 @@ int send_pack(struct send_pack_args *args,
                packet_flush(out);
        }
        strbuf_release(&req_buf);
+       strbuf_release(&cap_buf);
 
        if (use_sideband && cmds_sent) {
                memset(&demux, 0, sizeof(demux));
@@ -312,7 +419,7 @@ int send_pack(struct send_pack_args *args,
                in = demux.out;
        }
 
-       if (new_refs && cmds_sent) {
+       if (need_pack_data && cmds_sent) {
                if (pack_objects(out, remote_refs, extra_have, args) < 0) {
                        for (ref = remote_refs; ref; ref = ref->next)
                                ref->status = REF_STATUS_NONE;
index 8e843924cf5beba6ee680149910cd357cffc73e3..56354577467acfe1bf98652f6bfad9ad7e5db851 100644 (file)
@@ -2,6 +2,7 @@
 #define SEND_PACK_H
 
 struct send_pack_args {
+       const char *url;
        unsigned verbose:1,
                quiet:1,
                porcelain:1,
@@ -11,6 +12,7 @@ struct send_pack_args {
                use_thin_pack:1,
                use_ofs_delta:1,
                dry_run:1,
+               push_cert:1,
                stateless_rpc:1;
 };
 
index 5e8a207474bc6971a0de4f9ff6160a5681ac7b6d..1b9a35e5875683c9bd6ae7dba8e849201fdc0a42 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "sequencer.h"
 #include "dir.h"
 #include "object.h"
index 2dd851598a9d1444fe479d149ee3c2cb93e8cba9..5f069214d9060da8ceb001e6c037607c1d0096bf 100644 (file)
@@ -84,8 +84,6 @@ int sha1_pos(const unsigned char *sha1, void *table, size_t nr,
                                die("BUG: assertion failed in binary search");
                        }
                }
-               if (18 <= ofs)
-                       die("cannot happen -- lo and hi are identical");
        }
 
        do {
index c08c0cbea805b38104504b9b51266949affb6991..83f77f01b6370589fb90c72f111b4e6e381f9189 100644 (file)
@@ -8,6 +8,7 @@
  */
 #include "cache.h"
 #include "string-list.h"
+#include "lockfile.h"
 #include "delta.h"
 #include "pack.h"
 #include "blob.h"
@@ -663,10 +664,26 @@ void release_pack_memory(size_t need)
                ; /* nothing */
 }
 
+static void mmap_limit_check(size_t length)
+{
+       static size_t limit = 0;
+       if (!limit) {
+               limit = git_env_ulong("GIT_MMAP_LIMIT", 0);
+               if (!limit)
+                       limit = SIZE_MAX;
+       }
+       if (length > limit)
+               die("attempting to mmap %"PRIuMAX" over limit %"PRIuMAX,
+                   (uintmax_t)length, (uintmax_t)limit);
+}
+
 void *xmmap(void *start, size_t length,
        int prot, int flags, int fd, off_t offset)
 {
-       void *ret = mmap(start, length, prot, flags, fd, offset);
+       void *ret;
+
+       mmap_limit_check(length);
+       ret = mmap(start, length, prot, flags, fd, offset);
        if (ret == MAP_FAILED) {
                if (!length)
                        return NULL;
@@ -3076,6 +3093,29 @@ static int index_mem(unsigned char *sha1, void *buf, size_t size,
        return ret;
 }
 
+static int index_stream_convert_blob(unsigned char *sha1, int fd,
+                                    const char *path, unsigned flags)
+{
+       int ret;
+       const int write_object = flags & HASH_WRITE_OBJECT;
+       struct strbuf sbuf = STRBUF_INIT;
+
+       assert(path);
+       assert(would_convert_to_git_filter_fd(path));
+
+       convert_to_git_filter_fd(path, fd, &sbuf,
+                                write_object ? safe_crlf : SAFE_CRLF_FALSE);
+
+       if (write_object)
+               ret = write_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
+                                     sha1);
+       else
+               ret = hash_sha1_file(sbuf.buf, sbuf.len, typename(OBJ_BLOB),
+                                    sha1);
+       strbuf_release(&sbuf);
+       return ret;
+}
+
 static int index_pipe(unsigned char *sha1, int fd, enum object_type type,
                      const char *path, unsigned flags)
 {
@@ -3141,15 +3181,22 @@ int index_fd(unsigned char *sha1, int fd, struct stat *st,
             enum object_type type, const char *path, unsigned flags)
 {
        int ret;
-       size_t size = xsize_t(st->st_size);
 
-       if (!S_ISREG(st->st_mode))
+       /*
+        * Call xsize_t() only when needed to avoid potentially unnecessary
+        * die() for large files.
+        */
+       if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(path))
+               ret = index_stream_convert_blob(sha1, fd, path, flags);
+       else if (!S_ISREG(st->st_mode))
                ret = index_pipe(sha1, fd, type, path, flags);
-       else if (size <= big_file_threshold || type != OBJ_BLOB ||
-                (path && would_convert_to_git(path, NULL, 0, 0)))
-               ret = index_core(sha1, fd, size, type, path, flags);
+       else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
+                (path && would_convert_to_git(path)))
+               ret = index_core(sha1, fd, xsize_t(st->st_size), type, path,
+                                flags);
        else
-               ret = index_stream(sha1, fd, size, type, path, flags);
+               ret = index_stream(sha1, fd, xsize_t(st->st_size), type, path,
+                                  flags);
        close(fd);
        return ret;
 }
index 57f4afa6b40585c663d0df78494ce2108611ce4b..bd7569e815273b93b5862391c64694e141cc668d 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "commit.h"
 #include "tag.h"
 #include "pkt-line.h"
@@ -269,8 +270,8 @@ void setup_alternate_shallow(struct lock_file *shallow_lock,
        if (write_shallow_commits(&sb, 0, extra)) {
                if (write_in_full(fd, sb.buf, sb.len) != sb.len)
                        die_errno("failed to write to %s",
-                                 shallow_lock->filename);
-               *alternate_shallow_file = shallow_lock->filename;
+                                 shallow_lock->filename.buf);
+               *alternate_shallow_file = shallow_lock->filename.buf;
        } else
                /*
                 * is_repository_shallow() sees empty string as "no
@@ -316,7 +317,7 @@ void prune_shallow(int show_only)
        if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
                if (write_in_full(fd, sb.buf, sb.len) != sb.len)
                        die_errno("failed to write to %s",
-                                 shallow_lock.filename);
+                                 shallow_lock.filename.buf);
                commit_lock_file(&shallow_lock);
        } else {
                unlink(git_path("shallow"));
index 1118b99e57d3308f333c56f487329a8fef75a7df..faa375d5d8e7cf6bbba599034308028efaba1e0f 100644 (file)
@@ -1,5 +1,5 @@
-#include "sigchain.h"
 #include "cache.h"
+#include "sigchain.h"
 
 #define SIGCHAIN_MAX_SIGNALS 32
 
index 9e7d7962b0678dceefb93e3706b742cbfdc5b45e..d8e41f7ddd155b9f2861f55ab97943fed28968e7 100755 (executable)
@@ -278,12 +278,10 @@ helper_test_timeout() {
        '
 }
 
-cat >askpass <<\EOF
-#!/bin/sh
+write_script askpass <<\EOF
 echo >&2 askpass: $*
 what=$(echo $1 | cut -d" " -f1 | tr A-Z a-z | tr -cd a-z)
 echo "askpass-$what"
 EOF
-chmod +x askpass
 GIT_ASKPASS="$PWD/askpass"
 export GIT_ASKPASS
index b384d7993545365c17f3c8bded756b4e74faf794..7713dd260948c0fca0c0633b6c2cc152f7bc1361 100644 (file)
@@ -68,6 +68,7 @@ LockFile accept.lock
 
 PassEnv GIT_VALGRIND
 PassEnv GIT_VALGRIND_OPTIONS
+PassEnv GNUPGHOME
 
 Alias /dumb/ www/
 Alias /auth/dumb/ www/auth/dumb/
index f890c54d137aab021e6d6a61c5741af872555c1f..ca7d2a630a8442d8812b444708d6f28f2e45fb32 100755 (executable)
@@ -153,17 +153,23 @@ test_expect_success 'filter shell-escaped filenames' '
        :
 '
 
-test_expect_success 'required filter success' '
-       git config filter.required.smudge cat &&
-       git config filter.required.clean cat &&
+test_expect_success 'required filter should filter data' '
+       git config filter.required.smudge ./rot13.sh &&
+       git config filter.required.clean ./rot13.sh &&
        git config filter.required.required true &&
 
        echo "*.r filter=required" >.gitattributes &&
 
-       echo test >test.r &&
+       cat test.o >test.r &&
        git add test.r &&
+
        rm -f test.r &&
-       git checkout -- test.r
+       git checkout -- test.r &&
+       cmp test.o test.r &&
+
+       ./rot13.sh <test.o >expected &&
+       git cat-file blob :test.r >actual &&
+       cmp expected actual
 '
 
 test_expect_success 'required filter smudge failure' '
@@ -190,6 +196,14 @@ test_expect_success 'required filter clean failure' '
        test_must_fail git add test.fc
 '
 
+test_expect_success 'filtering large input to small output should use little memory' '
+       git config filter.devnull.clean "cat >/dev/null" &&
+       git config filter.devnull.required true &&
+       for i in $(test_seq 1 30); do printf "%1048576d" 1; done >30MB &&
+       echo "30MB filter=devnull" >.gitattributes &&
+       GIT_MMAP_LIMIT=1m GIT_ALLOC_LIMIT=1m git add 30MB
+'
+
 test_expect_success EXPENSIVE 'filter large file' '
        git config filter.largefile.smudge cat &&
        git config filter.largefile.clean cat &&
diff --git a/t/t0064-sha1-array.sh b/t/t0064-sha1-array.sh
new file mode 100755 (executable)
index 0000000..50b31ff
--- /dev/null
@@ -0,0 +1,94 @@
+#!/bin/sh
+
+test_description='basic tests for the SHA1 array implementation'
+. ./test-lib.sh
+
+echo20 () {
+       prefix="${1:+$1 }"
+       shift
+       while test $# -gt 0
+       do
+               echo "$prefix$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1$1"
+               shift
+       done
+}
+
+test_expect_success 'ordered enumeration' '
+       echo20 "" 44 55 88 aa >expect &&
+       {
+               echo20 append 88 44 aa 55 &&
+               echo for_each_unique
+       } | test-sha1-array >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'ordered enumeration with duplicate suppression' '
+       echo20 "" 44 55 88 aa >expect &&
+       {
+               echo20 append 88 44 aa 55 &&
+               echo20 append 88 44 aa 55 &&
+               echo for_each_unique
+       } | test-sha1-array >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'lookup' '
+       {
+               echo20 append 88 44 aa 55 &&
+               echo20 lookup 55
+       } | test-sha1-array >actual &&
+       n=$(cat actual) &&
+       test "$n" -eq 1
+'
+
+test_expect_success 'lookup non-existing entry' '
+       {
+               echo20 append 88 44 aa 55 &&
+               echo20 lookup 33
+       } | test-sha1-array >actual &&
+       n=$(cat actual) &&
+       test "$n" -lt 0
+'
+
+test_expect_success 'lookup with duplicates' '
+       {
+               echo20 append 88 44 aa 55 &&
+               echo20 append 88 44 aa 55 &&
+               echo20 lookup 55
+       } | test-sha1-array >actual &&
+       n=$(cat actual) &&
+       test "$n" -ge 2 &&
+       test "$n" -le 3
+'
+
+test_expect_success 'lookup non-existing entry with duplicates' '
+       {
+               echo20 append 88 44 aa 55 &&
+               echo20 append 88 44 aa 55 &&
+               echo20 lookup 66
+       } | test-sha1-array >actual &&
+       n=$(cat actual) &&
+       test "$n" -lt 0
+'
+
+test_expect_success 'lookup with almost duplicate values' '
+       {
+               echo "append 5555555555555555555555555555555555555555" &&
+               echo "append 555555555555555555555555555555555555555f" &&
+               echo20 lookup 55
+       } | test-sha1-array >actual &&
+       n=$(cat actual) &&
+       test "$n" -eq 0
+'
+
+test_expect_success 'lookup with single duplicate value' '
+       {
+               echo20 append 55 55 &&
+               echo20 lookup 55
+       } | test-sha1-array >actual &&
+       n=$(cat actual) &&
+       test "$n" -ge 0 &&
+       test "$n" -le 1
+'
+
+test_done
index f9648a86426470785159b5132305a256c80b5ca4..158cf4f03b63cf4eb61901dd1e178b3d1d08467a 100755 (executable)
@@ -22,7 +22,7 @@ generate_expected_cache_tree_rec () {
        # ls-files might have foo/bar, foo/bar/baz, and foo/bar/quux
        # We want to count only foo because it's the only direct child
        subtrees=$(git ls-files|grep /|cut -d / -f 1|uniq) &&
-       subtree_count=$(echo "$subtrees"|awk '$1 {++c} END {print c}') &&
+       subtree_count=$(echo "$subtrees"|awk -v c=0 '$1 {++c} END {print c}') &&
        entries=$(git ls-files|wc -l) &&
        printf "SHA $dir (%d entries, %d subtrees)\n" "$entries" "$subtree_count" &&
        for subtree in $subtrees
index 05a1e1d270d2f6254658607541f1b114299533e5..f5a91192903b455df3d7d13d8009ea90999405e3 100755 (executable)
@@ -13,7 +13,7 @@ test_expect_success setup '
        echo X | dd of=large2 bs=1k seek=2000 &&
        echo X | dd of=large3 bs=1k seek=2000 &&
        echo Y | dd of=huge bs=1k seek=2500 &&
-       GIT_ALLOC_LIMIT=1500 &&
+       GIT_ALLOC_LIMIT=1500k &&
        export GIT_ALLOC_LIMIT
 '
 
index 7b8babd89b22dc94d3a3f839d72cce86c5aaea59..d01bbdc968a0fcde9eb5410793f8a22108ee6c2d 100755 (executable)
@@ -305,4 +305,18 @@ test_expect_success GZIP 'remote tar.gz can be disabled' '
                >remote.tar.gz
 '
 
+test_expect_success 'archive and :(glob)' '
+       git archive -v HEAD -- ":(glob)**/sh" >/dev/null 2>actual &&
+       cat >expect <<EOF &&
+a/
+a/bin/
+a/bin/sh
+EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'catch non-matching pathspec' '
+       test_must_fail git archive -v HEAD -- "*.abc" >/dev/null
+'
+
 test_done
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
new file mode 100755 (executable)
index 0000000..2786346
--- /dev/null
@@ -0,0 +1,127 @@
+#!/bin/sh
+
+test_description='signed push'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-gpg.sh
+
+prepare_dst () {
+       rm -fr dst &&
+       test_create_repo dst &&
+
+       git push dst master:noop master:ff master:noff
+}
+
+test_expect_success setup '
+       # master, ff and noff branches pointing at the same commit
+       test_tick &&
+       git commit --allow-empty -m initial &&
+
+       git checkout -b noop &&
+       git checkout -b ff &&
+       git checkout -b noff &&
+
+       # noop stays the same, ff advances, noff rewrites
+       test_tick &&
+       git commit --allow-empty --amend -m rewritten &&
+       git checkout ff &&
+
+       test_tick &&
+       git commit --allow-empty -m second
+'
+
+test_expect_success 'unsigned push does not send push certificate' '
+       prepare_dst &&
+       mkdir -p dst/.git/hooks &&
+       write_script dst/.git/hooks/post-receive <<-\EOF &&
+       # discard the update list
+       cat >/dev/null
+       # record the push certificate
+       if test -n "${GIT_PUSH_CERT-}"
+       then
+               git cat-file blob $GIT_PUSH_CERT >../push-cert
+       fi
+       EOF
+
+       git push dst noop ff +noff &&
+       ! test -f dst/push-cert
+'
+
+test_expect_success 'talking with a receiver without push certificate support' '
+       prepare_dst &&
+       mkdir -p dst/.git/hooks &&
+       write_script dst/.git/hooks/post-receive <<-\EOF &&
+       # discard the update list
+       cat >/dev/null
+       # record the push certificate
+       if test -n "${GIT_PUSH_CERT-}"
+       then
+               git cat-file blob $GIT_PUSH_CERT >../push-cert
+       fi
+       EOF
+
+       git push dst noop ff +noff &&
+       ! test -f dst/push-cert
+'
+
+test_expect_success 'push --signed fails with a receiver without push certificate support' '
+       prepare_dst &&
+       mkdir -p dst/.git/hooks &&
+       test_must_fail git push --signed dst noop ff +noff 2>err &&
+       test_i18ngrep "the receiving end does not support" err
+'
+
+test_expect_success GPG 'no certificate for a signed push with no update' '
+       prepare_dst &&
+       mkdir -p dst/.git/hooks &&
+       write_script dst/.git/hooks/post-receive <<-\EOF &&
+       if test -n "${GIT_PUSH_CERT-}"
+       then
+               git cat-file blob $GIT_PUSH_CERT >../push-cert
+       fi
+       EOF
+       git push dst noop &&
+       ! test -f dst/push-cert
+'
+
+test_expect_success GPG 'signed push sends push certificate' '
+       prepare_dst &&
+       mkdir -p dst/.git/hooks &&
+       git -C dst config receive.certnonceseed sekrit &&
+       write_script dst/.git/hooks/post-receive <<-\EOF &&
+       # discard the update list
+       cat >/dev/null
+       # record the push certificate
+       if test -n "${GIT_PUSH_CERT-}"
+       then
+               git cat-file blob $GIT_PUSH_CERT >../push-cert
+       fi &&
+
+       cat >../push-cert-status <<E_O_F
+       SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+       KEY=${GIT_PUSH_CERT_KEY-nokey}
+       STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+       NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+       NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+       E_O_F
+
+       EOF
+
+       git push --signed dst noop ff +noff &&
+
+       (
+               cat <<-\EOF &&
+               SIGNER=C O Mitter <committer@example.com>
+               KEY=13B6F51ECDDE430D
+               STATUS=G
+               NONCE_STATUS=OK
+               EOF
+               sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+       ) >expect &&
+
+       grep "$(git rev-parse noop ff) refs/heads/ff" dst/push-cert &&
+       grep "$(git rev-parse noop noff) refs/heads/noff" dst/push-cert &&
+       test_cmp expect dst/push-cert-status
+'
+
+test_done
index db1998873cee18a74d98d13963ad640bc1f8abbe..d2c681ebfde39fcccef190c3a242dfae9d8af2f2 100755 (executable)
@@ -12,6 +12,7 @@ if test -n "$NO_CURL"; then
 fi
 
 ROOT_PATH="$PWD"
+. "$TEST_DIRECTORY"/lib-gpg.sh
 . "$TEST_DIRECTORY"/lib-httpd.sh
 . "$TEST_DIRECTORY"/lib-terminal.sh
 start_httpd
@@ -338,5 +339,45 @@ test_expect_success CMDLINE_LIMIT 'push 2000 tags over http' '
        run_with_limited_cmdline git push --mirror
 '
 
+test_expect_success GPG 'push with post-receive to inspect certificate' '
+       (
+               cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
+               mkdir -p hooks &&
+               write_script hooks/post-receive <<-\EOF &&
+               # discard the update list
+               cat >/dev/null
+               # record the push certificate
+               if test -n "${GIT_PUSH_CERT-}"
+               then
+                       git cat-file blob $GIT_PUSH_CERT >../push-cert
+               fi &&
+               cat >../push-cert-status <<E_O_F
+               SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+               KEY=${GIT_PUSH_CERT_KEY-nokey}
+               STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+               NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+               NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+               E_O_F
+               EOF
+
+               git config receive.certnonceseed sekrit &&
+               git config receive.certnonceslop 30
+       ) &&
+       cd "$ROOT_PATH/test_repo_clone" &&
+       test_commit cert-test &&
+       git push --signed "$HTTPD_URL/smart/test_repo.git" &&
+       (
+               cd "$HTTPD_DOCUMENT_ROOT_PATH" &&
+               cat <<-\EOF &&
+               SIGNER=C O Mitter <committer@example.com>
+               KEY=13B6F51ECDDE430D
+               STATUS=G
+               NONCE_STATUS=OK
+               EOF
+               sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" push-cert
+       ) >expect &&
+       test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH/push-cert-status"
+'
+
 stop_httpd
 test_done
index 0366653088417a3d694856538bb1d1bd35d36cda..796e9f79eadfdf5b641d429e2083dd7d0c27332c 100755 (executable)
@@ -1460,7 +1460,7 @@ test_expect_success 'invalid sort parameter in configuratoin' '
 '
 
 run_with_limited_stack () {
-       (ulimit -s 64 && "$@")
+       (ulimit -s 128 && "$@")
 }
 
 test_lazy_prereq ULIMIT 'run_with_limited_stack true'
@@ -1469,7 +1469,7 @@ test_lazy_prereq ULIMIT 'run_with_limited_stack true'
 test_expect_success ULIMIT '--contains works in a deep repo' '
        >expect &&
        i=1 &&
-       while test $i -lt 4000
+       while test $i -lt 8000
        do
                echo "commit refs/heads/master
 committer A U Thor <author@example.com> $((1000000000 + $i * 100)) +0200
index 82095e34eeb7ce9932abe1a5e97b1c2eb0888033..0f4a67bfc63a989883007911f889ba5aed1c0aac 100644 (file)
@@ -813,7 +813,8 @@ rm -fr "$TRASH_DIRECTORY" || {
 }
 
 HOME="$TRASH_DIRECTORY"
-export HOME
+GNUPGHOME="$HOME/gnupg-home-not-used"
+export HOME GNUPGHOME
 
 if test -z "$TEST_NO_CREATE_REPO"
 then
diff --git a/tag.c b/tag.c
index 82d841bf2df0990f124ca9020673352124f20561..5b0ac62ed846188ad44ceac41d278ff37ec8bd9d 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -4,9 +4,6 @@
 #include "tree.h"
 #include "blob.h"
 
-#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
-#define PGP_MESSAGE "-----BEGIN PGP MESSAGE-----"
-
 const char *tag_type = "tag";
 
 struct object *deref_tag(struct object *o, const char *warn, int warnlen)
@@ -143,20 +140,3 @@ int parse_tag(struct tag *item)
        free(data);
        return ret;
 }
-
-/*
- * Look at a signed tag object, and return the offset where
- * the embedded detached signature begins, or the end of the
- * data when there is no such signature.
- */
-size_t parse_signature(const char *buf, unsigned long size)
-{
-       char *eol;
-       size_t len = 0;
-       while (len < size && !starts_with(buf + len, PGP_SIGNATURE) &&
-                       !starts_with(buf + len, PGP_MESSAGE)) {
-               eol = memchr(buf + len, '\n', size - len);
-               len += eol ? eol - (buf + len) + 1 : size - len;
-       }
-       return len;
-}
diff --git a/tag.h b/tag.h
index bc8a1e40f04e87a6d502ab9d96022f734c57f4eb..f4580aea42633d4e6d849735cf1eedcbc4730ed2 100644 (file)
--- a/tag.h
+++ b/tag.h
@@ -17,6 +17,5 @@ extern int parse_tag_buffer(struct tag *item, const void *data, unsigned long si
 extern int parse_tag(struct tag *item);
 extern struct object *deref_tag(struct object *, const char *, int);
 extern struct object *deref_tag_noverify(struct object *);
-extern size_t parse_signature(const char *buf, unsigned long size);
 
 #endif /* TAG_H */
index b5bfd54139fda811a30a4e56a7098bccba7039ce..0dc598ecdc2696af956b1c517166f9e28b37dc68 100644 (file)
@@ -1,4 +1,4 @@
-#include <git-compat-util.h>
+#include "git-compat-util.h"
 
 int main(int argc, char **argv)
 {
index 9ebcbca9d25150e9bfca77a73ca45d2a06423f49..6efee31a4867b4ff8493161376e5a9cfdd48fe44 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "lockfile.h"
 #include "tree.h"
 #include "cache-tree.h"
 
diff --git a/test-sha1-array.c b/test-sha1-array.c
new file mode 100644 (file)
index 0000000..ddc491e
--- /dev/null
@@ -0,0 +1,34 @@
+#include "cache.h"
+#include "sha1-array.h"
+
+static void print_sha1(const unsigned char sha1[20], void *data)
+{
+       puts(sha1_to_hex(sha1));
+}
+
+int main(int argc, char **argv)
+{
+       struct sha1_array array = SHA1_ARRAY_INIT;
+       struct strbuf line = STRBUF_INIT;
+
+       while (strbuf_getline(&line, stdin, '\n') != EOF) {
+               const char *arg;
+               unsigned char sha1[20];
+
+               if (skip_prefix(line.buf, "append ", &arg)) {
+                       if (get_sha1_hex(arg, sha1))
+                               die("not a hexadecimal SHA1: %s", arg);
+                       sha1_array_append(&array, sha1);
+               } else if (skip_prefix(line.buf, "lookup ", &arg)) {
+                       if (get_sha1_hex(arg, sha1))
+                               die("not a hexadecimal SHA1: %s", arg);
+                       printf("%d\n", sha1_array_lookup(&array, sha1));
+               } else if (!strcmp(line.buf, "clear"))
+                       sha1_array_clear(&array);
+               else if (!strcmp(line.buf, "for_each_unique"))
+                       sha1_array_for_each_unique(&array, print_sha1, NULL);
+               else
+                       die("unknown command: %s", line.buf);
+       }
+       return 0;
+}
index 42db234e871908514814c9b8dc50cc65d9b093cd..e499fce60ff50069ace6174ef9fa3ca4aff0cdc8 100644 (file)
@@ -1,5 +1,5 @@
-#include "sigchain.h"
 #include "cache.h"
+#include "sigchain.h"
 
 #define X(f) \
 static void f(int sig) { \
index 080a7a6ae23e6915c425a3e471fd641aabafba90..2b24d51a24adb9dfc885591ceb1653ace65f198a 100644 (file)
@@ -260,7 +260,8 @@ static const char *unsupported_options[] = {
 static const char *boolean_options[] = {
        TRANS_OPT_THIN,
        TRANS_OPT_KEEP,
-       TRANS_OPT_FOLLOWTAGS
+       TRANS_OPT_FOLLOWTAGS,
+       TRANS_OPT_PUSH_CERT
        };
 
 static int set_helper_option(struct transport *transport,
@@ -836,6 +837,9 @@ static int push_refs_with_push(struct transport *transport,
        if (flags & TRANSPORT_PUSH_DRY_RUN) {
                if (set_helper_option(transport, "dry-run", "true") != 0)
                        die("helper %s does not support dry-run", data->name);
+       } else if (flags & TRANSPORT_PUSH_CERT) {
+               if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0)
+                       die("helper %s does not support --signed", data->name);
        }
 
        strbuf_addch(&buf, '\n');
@@ -860,6 +864,9 @@ static int push_refs_with_export(struct transport *transport,
        if (flags & TRANSPORT_PUSH_DRY_RUN) {
                if (set_helper_option(transport, "dry-run", "true") != 0)
                        die("helper %s does not support dry-run", data->name);
+       } else if (flags & TRANSPORT_PUSH_CERT) {
+               if (set_helper_option(transport, TRANS_OPT_PUSH_CERT, "true") != 0)
+                       die("helper %s does not support dry-run", data->name);
        }
 
        if (flags & TRANSPORT_PUSH_FORCE) {
index 7388bb87dae96514801bcd604b74d31f54955f9a..055d2a27d945de69f20a890d5e184b7bd09f93d9 100644 (file)
@@ -477,6 +477,9 @@ static int set_git_option(struct git_transport_options *opts,
                                die("transport: invalid depth option '%s'", value);
                }
                return 0;
+       } else if (!strcmp(name, TRANS_OPT_PUSH_CERT)) {
+               opts->push_cert = !!value;
+               return 0;
        }
        return 1;
 }
@@ -820,6 +823,8 @@ static int git_transport_push(struct transport *transport, struct ref *remote_re
        args.progress = transport->progress;
        args.dry_run = !!(flags & TRANSPORT_PUSH_DRY_RUN);
        args.porcelain = !!(flags & TRANSPORT_PUSH_PORCELAIN);
+       args.push_cert = !!(flags & TRANSPORT_PUSH_CERT);
+       args.url = transport->url;
 
        ret = send_pack(&args, data->fd, data->conn, remote_refs,
                        &data->extra_have);
index 02ea248db18a558674825384a5d2678a45483873..3e0091eaabe406759005bc24ce2ff39144caa72d 100644 (file)
@@ -12,6 +12,7 @@ struct git_transport_options {
        unsigned check_self_contained_and_connected : 1;
        unsigned self_contained_and_connected : 1;
        unsigned update_shallow : 1;
+       unsigned push_cert : 1;
        int depth;
        const char *uploadpack;
        const char *receivepack;
@@ -123,6 +124,7 @@ struct transport {
 #define TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND 256
 #define TRANSPORT_PUSH_NO_HOOK 512
 #define TRANSPORT_PUSH_FOLLOW_TAGS 1024
+#define TRANSPORT_PUSH_CERT 2048
 
 #define TRANSPORT_SUMMARY_WIDTH (2 * DEFAULT_ABBREV + 3)
 #define TRANSPORT_SUMMARY(x) (int)(TRANSPORT_SUMMARY_WIDTH + strlen(x) - gettext_width(x)), (x)
@@ -156,6 +158,9 @@ struct transport *transport_get(struct remote *, const char *);
 /* Accept refs that may update .git/shallow without --depth */
 #define TRANS_OPT_UPDATE_SHALLOW "updateshallow"
 
+/* Send push certificates */
+#define TRANS_OPT_PUSH_CERT "pushcert"
+
 /**
  * Returns 0 if the option was used, non-zero otherwise. Prints a
  * message to stderr if the option is not used.
index 4ed772949651005055dadd236cee8d5d5c8f2492..409c4977a1e3a3a72dcd1452b41abdcb5a42b665 100644 (file)
--- a/varint.c
+++ b/varint.c
@@ -1,3 +1,4 @@
+#include "git-compat-util.h"
 #include "varint.h"
 
 uintmax_t decode_varint(const unsigned char **bufp)
index 032119579694f6a82f3947bfafb05a801385cb5a..c1c44d9a6bde4853f55d6c8d7cd59ad159ef4abb 100644 (file)
--- a/varint.h
+++ b/varint.h
@@ -1,8 +1,6 @@
 #ifndef VARINT_H
 #define VARINT_H
 
-#include "git-compat-util.h"
-
 extern int encode_varint(uintmax_t, unsigned char *);
 extern uintmax_t decode_varint(const unsigned char **);
 
index 25074d71b6ce72066efc02abda74ddfb10f71d6c..5b77d2a1aecd494ea4bb5eb1e6f31afc9c96f82e 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -11,19 +11,20 @@ static void (*try_to_free_routine)(size_t size) = do_nothing;
 
 static int memory_limit_check(size_t size, int gentle)
 {
-       static int limit = -1;
-       if (limit == -1) {
-               const char *env = getenv("GIT_ALLOC_LIMIT");
-               limit = env ? atoi(env) * 1024 : 0;
+       static size_t limit = 0;
+       if (!limit) {
+               limit = git_env_ulong("GIT_ALLOC_LIMIT", 0);
+               if (!limit)
+                       limit = SIZE_MAX;
        }
-       if (limit && size > limit) {
+       if (size > limit) {
                if (gentle) {
-                       error("attempting to allocate %"PRIuMAX" over limit %d",
-                             (intmax_t)size, limit);
+                       error("attempting to allocate %"PRIuMAX" over limit %"PRIuMAX,
+                             (uintmax_t)size, (uintmax_t)limit);
                        return -1;
                } else
-                       die("attempting to allocate %"PRIuMAX" over limit %d",
-                           (intmax_t)size, limit);
+                       die("attempting to allocate %"PRIuMAX" over limit %"PRIuMAX,
+                           (uintmax_t)size, (uintmax_t)limit);
        }
        return 0;
 }