Merge branch 'jk/negative-hiderefs'
authorJunio C Hamano <gitster@pobox.com>
Wed, 19 Aug 2015 21:48:53 +0000 (14:48 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 19 Aug 2015 21:48:54 +0000 (14:48 -0700)
A negative !ref entry in multi-value transfer.hideRefs
configuration can be used to say "don't hide this one".

* jk/negative-hiderefs:
refs: support negative transfer.hideRefs
docs/config.txt: reorder hideRefs config

1  2 
Documentation/config.txt
refs.c
diff --combined Documentation/config.txt
index 016f6e9f952fc4aee60bd77565164b20344be89f,a7fbd0aac2933cc0fc8f5f71aca57b35cc5b93b1..75ec02e8e90a57a54a768677a272975487f664a8
@@@ -769,14 -769,6 +769,14 @@@ am.keepcr:
        by giving '--no-keep-cr' from the command line.
        See linkgit:git-am[1], linkgit:git-mailsplit[1].
  
 +am.threeWay::
 +      By default, `git am` will fail if the patch does not apply cleanly. When
 +      set to true, this setting tells `git am` to fall back on 3-way merge if
 +      the patch records the identity of blobs it is supposed to apply to and
 +      we have those blobs available locally (equivalent to giving the `--3way`
 +      option from the command line). Defaults to `false`.
 +      See linkgit:git-am[1].
 +
  apply.ignoreWhitespace::
        When set to 'change', tells 'git apply' to ignore changes in
        whitespace, in the same way as the '--ignore-space-change'
@@@ -1250,25 -1242,6 +1250,25 @@@ filter.<driver>.smudge:
        object to a worktree file upon checkout.  See
        linkgit:gitattributes[5] for details.
  
 +fsck.<msg-id>::
 +      Allows overriding the message type (error, warn or ignore) of a
 +      specific message ID such as `missingEmail`.
 ++
 +For convenience, fsck prefixes the error/warning with the message ID,
 +e.g.  "missingEmail: invalid author/committer line - missing email" means
 +that setting `fsck.missingEmail = ignore` will hide that issue.
 ++
 +This feature is intended to support working with legacy repositories
 +which cannot be repaired without disruptive changes.
 +
 +fsck.skipList::
 +      The path to a sorted list of object names (i.e. one SHA-1 per
 +      line) that are known to be broken in a non-fatal way and should
 +      be ignored. This feature is useful when an established project
 +      should be accepted despite early commits containing errors that
 +      can be safely ignored such as invalid committer email addresses.
 +      Note: corrupt objects cannot be skipped with this setting.
 +
  gc.aggressiveDepth::
        The depth parameter used in the delta compression
        algorithm used by 'git gc --aggressive'.  This defaults
@@@ -1307,24 -1280,20 +1307,24 @@@ gc.packRefs:
  gc.pruneExpire::
        When 'git gc' is run, it will call 'prune --expire 2.weeks.ago'.
        Override the grace period with this config variable.  The value
 -      "now" may be used to disable this  grace period and always prune
 -      unreachable objects immediately.
 -
 -gc.pruneWorktreesExpire::
 -      When 'git gc' is run, it will call
 -      'prune --worktrees --expire 3.months.ago'.
 -      Override the grace period with this config variable. The value
 -      "now" may be used to disable the grace period and prune
 -      $GIT_DIR/worktrees immediately.
 +      "now" may be used to disable this grace period and always prune
 +      unreachable objects immediately, or "never" may be used to
 +      suppress pruning.
 +
 +gc.worktreePruneExpire::
 +      When 'git gc' is run, it calls
 +      'git worktree prune --expire 3.months.ago'.
 +      This config variable can be used to set a different grace
 +      period. The value "now" may be used to disable the grace
 +      period and prune $GIT_DIR/worktrees immediately, or "never"
 +      may be used to suppress pruning.
  
  gc.reflogExpire::
  gc.<pattern>.reflogExpire::
        'git reflog expire' removes reflog entries older than
 -      this time; defaults to 90 days.  With "<pattern>" (e.g.
 +      this time; defaults to 90 days. The value "now" expires all
 +      entries immediately, and "never" suppresses expiration
 +      altogether. With "<pattern>" (e.g.
        "refs/stash") in the middle the setting applies only to
        the refs that match the <pattern>.
  
@@@ -1332,9 -1301,7 +1332,9 @@@ gc.reflogExpireUnreachable:
  gc.<ref>.reflogExpireUnreachable::
        'git reflog expire' removes reflog entries older than
        this time and are not reachable from the current tip;
 -      defaults to 30 days.  With "<pattern>" (e.g. "refs/stash")
 +      defaults to 30 days. The value "now" expires all entries
 +      immediately, and "never" suppresses expiration altogether.
 +      With "<pattern>" (e.g. "refs/stash")
        in the middle, the setting applies only to the refs that
        match the <pattern>.
  
@@@ -2194,22 -2161,6 +2194,22 @@@ rebase.autoStash:
        successful rebase might result in non-trivial conflicts.
        Defaults to false.
  
 +rebase.missingCommitsCheck::
 +      If set to "warn", git rebase -i will print a warning if some
 +      commits are removed (e.g. a line was deleted), however the
 +      rebase will still proceed. If set to "error", it will print
 +      the previous warning and stop the rebase, 'git rebase
 +      --edit-todo' can then be used to correct the error. If set to
 +      "ignore", no checking is done.
 +      To drop a commit without warning or error, use the `drop`
 +      command in the todo-list.
 +      Defaults to "ignore".
 +
 +rebase.instructionFormat
 +      A format string, as specified in linkgit:git-log[1], to be used for
 +      the instruction list during an interactive rebase.  The format will automatically
 +      have the long commit hash prepended to the format.
 +
  receive.advertiseAtomic::
        By default, git-receive-pack will advertise the atomic push
        capability to its clients. If you don't want to this capability
@@@ -2246,28 -2197,6 +2246,28 @@@ receive.fsckObjects:
        Defaults to false. If not set, the value of `transfer.fsckObjects`
        is used instead.
  
 +receive.fsck.<msg-id>::
 +      When `receive.fsckObjects` is set to true, errors can be switched
 +      to warnings and vice versa by configuring the `receive.fsck.<msg-id>`
 +      setting where the `<msg-id>` is the fsck message ID and the value
 +      is one of `error`, `warn` or `ignore`. For convenience, fsck prefixes
 +      the error/warning with the message ID, e.g. "missingEmail: invalid
 +      author/committer line - missing email" means that setting
 +      `receive.fsck.missingEmail = ignore` will hide that issue.
 ++
 +This feature is intended to support working with legacy repositories
 +which would not pass pushing when `receive.fsckObjects = true`, allowing
 +the host to accept repositories with certain known issues but still catch
 +other issues.
 +
 +receive.fsck.skipList::
 +      The path to a sorted list of object names (i.e. one SHA-1 per
 +      line) that are known to be broken in a non-fatal way and should
 +      be ignored. This feature is useful when an established project
 +      should be accepted despite early commits containing errors that
 +      can be safely ignored such as invalid committer email addresses.
 +      Note: corrupt objects cannot be skipped with this setting.
 +
  receive.unpackLimit::
        If the number of objects received in a push is below this
        limit then the objects will be unpacked into loose object
@@@ -2313,13 -2242,10 +2313,10 @@@ receive.denyNonFastForwards:
        set when initializing a shared repository.
  
  receive.hideRefs::
-       String(s) `receive-pack` uses to decide which refs to omit
-       from its initial advertisement.  Use more than one
-       definitions to specify multiple prefix strings. A ref that
-       are under the hierarchies listed on the value of this
-       variable is excluded, and is hidden when responding to `git
-       push`, and an attempt to update or delete a hidden ref by
-       `git push` is rejected.
+       This variable is the same as `transfer.hideRefs`, but applies
+       only to `receive-pack` (and so affects pushes, but not fetches).
+       An attempt to update or delete a hidden ref by `git push` is
+       rejected.
  
  receive.updateServerInfo::
        If set to true, git-receive-pack will run git-update-server-info
@@@ -2607,9 -2533,18 +2604,18 @@@ transfer.fsckObjects:
        Defaults to false.
  
  transfer.hideRefs::
-       This variable can be used to set both `receive.hideRefs`
-       and `uploadpack.hideRefs` at the same time to the same
-       values.  See entries for these other variables.
+       String(s) `receive-pack` and `upload-pack` use to decide which
+       refs to omit from their initial advertisements.  Use more than
+       one definition to specify multiple prefix strings. A ref that is
+       under the hierarchies listed in the value of this variable is
+       excluded, and is hidden when responding to `git push` or `git
+       fetch`.  See `receive.hideRefs` and `uploadpack.hideRefs` for
+       program-specific versions of this config.
+ +
+ You may also include a `!` in front of the ref name to negate the entry,
+ explicitly exposing it, even if an earlier entry marked it as hidden.
+ If you have multiple hideRefs values, later entries override earlier ones
+ (and entries in more-specific config files override less-specific ones).
  
  transfer.unpackLimit::
        When `fetch.unpackLimit` or `receive.unpackLimit` are
@@@ -2624,13 -2559,10 +2630,10 @@@ uploadarchive.allowUnreachable:
        `false`.
  
  uploadpack.hideRefs::
-       String(s) `upload-pack` uses to decide which refs to omit
-       from its initial advertisement.  Use more than one
-       definitions to specify multiple prefix strings. A ref that
-       are under the hierarchies listed on the value of this
-       variable is excluded, and is hidden from `git ls-remote`,
-       `git fetch`, etc.  An attempt to fetch a hidden ref by `git
-       fetch` will fail.  See also `uploadpack.allowTipSHA1InWant`.
+       This variable is the same as `transfer.hideRefs`, but applies
+       only to `upload-pack` (and so affects only fetches, not pushes).
+       An attempt to fetch a hidden ref by `git fetch` will fail.  See
+       also `uploadpack.allowTipSHA1InWant`.
  
  uploadpack.allowTipSHA1InWant::
        When `uploadpack.hideRefs` is in effect, allow `upload-pack`
diff --combined refs.c
index 2db2975e08b7cccd644eab6b4b0ad752e4ec02b3,68f2cb03c4031a525b99ea5a3494a9c64d786c83..5f9831e66b7a3406748b8a6bd0685d6e9451bf61
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -19,14 -19,12 +19,14 @@@ struct ref_lock 
   * 1: End-of-component
   * 2: ., look for a preceding . to reject .. in refs
   * 3: {, look for a preceding @ to reject @{ in refs
 - * 4: A bad character: ASCII control characters, "~", "^", ":" or SP
 + * 4: A bad character: ASCII control characters, and
 + *    ":", "?", "[", "\", "^", "~", SP, or TAB
 + * 5: *, reject unless REFNAME_REFSPEC_PATTERN is set
   */
  static unsigned char refname_disposition[256] = {
        1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
        4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
 -      4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
 +      4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
   */
  #define REF_NEEDS_COMMIT 0x20
  
 +/*
 + * 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a
 + * value to ref_update::flags
 + */
 +
  /*
   * Try to read one refname component from the front of refname.
   * Return the length of the component found, or -1 if the component is
   *
   * - any path component of it begins with ".", or
   * - it has double dots "..", or
 - * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
 - * - it ends with a "/".
 - * - it ends with ".lock"
 - * - it contains a "\" (backslash)
 + * - it has ASCII control characters, or
 + * - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
 + * - it has "*" anywhere unless REFNAME_REFSPEC_PATTERN is set, or
 + * - it ends with a "/", or
 + * - it ends with ".lock", or
 + * - it contains a "@{" portion
   */
 -static int check_refname_component(const char *refname, int flags)
 +static int check_refname_component(const char *refname, int *flags)
  {
        const char *cp;
        char last = '\0';
                        break;
                case 4:
                        return -1;
 +              case 5:
 +                      if (!(*flags & REFNAME_REFSPEC_PATTERN))
 +                              return -1; /* refspec can't be a pattern */
 +
 +                      /*
 +                       * Unset the pattern flag so that we only accept
 +                       * a single asterisk for one side of refspec.
 +                       */
 +                      *flags &= ~ REFNAME_REFSPEC_PATTERN;
 +                      break;
                }
                last = ch;
        }
@@@ -139,10 -120,18 +139,10 @@@ int check_refname_format(const char *re
  
        while (1) {
                /* We are at the start of a path component. */
 -              component_len = check_refname_component(refname, flags);
 -              if (component_len <= 0) {
 -                      if ((flags & REFNAME_REFSPEC_PATTERN) &&
 -                                      refname[0] == '*' &&
 -                                      (refname[1] == '\0' || refname[1] == '/')) {
 -                              /* Accept one wildcard as a full refname component. */
 -                              flags &= ~REFNAME_REFSPEC_PATTERN;
 -                              component_len = 1;
 -                      } else {
 -                              return -1;
 -                      }
 -              }
 +              component_len = check_refname_component(refname, &flags);
 +              if (component_len <= 0)
 +                      return -1;
 +
                component_count++;
                if (refname[component_len] == '\0')
                        break;
@@@ -1325,13 -1314,7 +1325,13 @@@ static struct ref_dir *get_packed_refs(
        return get_packed_ref_dir(get_packed_ref_cache(refs));
  }
  
 -void add_packed_ref(const char *refname, const unsigned char *sha1)
 +/*
 + * Add a reference to the in-memory packed reference cache.  This may
 + * only be called while the packed-refs file is locked (see
 + * lock_packed_refs()).  To actually write the packed-refs file, call
 + * commit_packed_refs().
 + */
 +static void add_packed_ref(const char *refname, const unsigned char *sha1)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
@@@ -1758,11 -1741,9 +1758,11 @@@ const char *resolve_ref_unsafe(const ch
        return ret;
  }
  
 -char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags)
 +char *resolve_refdup(const char *refname, int resolve_flags,
 +                   unsigned char *sha1, int *flags)
  {
 -      return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags));
 +      return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,
 +                                                sha1, flags));
  }
  
  /* The argument to filter_refs */
@@@ -2141,8 -2122,7 +2141,8 @@@ int for_each_remote_ref_submodule(cons
  
  int for_each_replace_ref(each_ref_fn fn, void *cb_data)
  {
 -      return do_for_each_ref(&ref_cache, "refs/replace/", fn, 13, 0, cb_data);
 +      return do_for_each_ref(&ref_cache, git_replace_ref_base, fn,
 +                             strlen(git_replace_ref_base), 0, cb_data);
  }
  
  int head_ref_namespaced(each_ref_fn fn, void *cb_data)
@@@ -2550,12 -2530,8 +2550,12 @@@ static int write_packed_entry_fn(struc
        return 0;
  }
  
 -/* This should return a meaningful errno on failure */
 -int lock_packed_refs(int flags)
 +/*
 + * Lock the packed-refs file for writing. Flags is passed to
 + * hold_lock_file_for_update(). Return 0 on success. On errors, set
 + * errno appropriately and return a nonzero value.
 + */
 +static int lock_packed_refs(int flags)
  {
        static int timeout_configured = 0;
        static int timeout_value = 1000;
  }
  
  /*
 - * Commit the packed refs changes.
 - * On error we must make sure that errno contains a meaningful value.
 + * Write the current version of the packed refs cache from memory to
 + * disk. The packed-refs file must already be locked for writing (see
 + * lock_packed_refs()). Return zero on success. On errors, set errno
 + * and return a nonzero value
   */
 -int commit_packed_refs(void)
 +static int commit_packed_refs(void)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
        return error;
  }
  
 -void rollback_packed_refs(void)
 +/*
 + * Rollback the lockfile for the packed-refs file, and discard the
 + * in-memory packed reference cache.  (The packed-refs file will be
 + * read anew if it is needed again after this function is called.)
 + */
 +static void rollback_packed_refs(void)
  {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(&ref_cache);
@@@ -2782,14 -2751,7 +2782,14 @@@ int pack_refs(unsigned int flags
        return 0;
  }
  
 -int repack_without_refs(struct string_list *refnames, struct strbuf *err)
 +/*
 + * Rewrite the packed-refs file, omitting any refs listed in
 + * 'refnames'. On error, leave packed-refs unchanged, write an error
 + * message to 'err', and return a nonzero value.
 + *
 + * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
 + */
 +static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
  {
        struct ref_dir *packed;
        struct string_list_item *refname;
@@@ -2854,15 -2816,15 +2854,15 @@@ static int delete_ref_loose(struct ref_
        return 0;
  }
  
 -int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flags)
 +int delete_ref(const char *refname, const unsigned char *old_sha1,
 +             unsigned int flags)
  {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
  
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
 -          ref_transaction_delete(transaction, refname,
 -                                 (sha1 && !is_null_sha1(sha1)) ? sha1 : NULL,
 +          ref_transaction_delete(transaction, refname, old_sha1,
                                   flags, NULL, &err) ||
            ref_transaction_commit(transaction, &err)) {
                error("%s", err.buf);
        return 0;
  }
  
 +int delete_refs(struct string_list *refnames)
 +{
 +      struct strbuf err = STRBUF_INIT;
 +      int i, result = 0;
 +
 +      if (!refnames->nr)
 +              return 0;
 +
 +      result = repack_without_refs(refnames, &err);
 +      if (result) {
 +              /*
 +               * If we failed to rewrite the packed-refs file, then
 +               * it is unsafe to try to remove loose refs, because
 +               * doing so might expose an obsolete packed value for
 +               * a reference that might even point at an object that
 +               * has been garbage collected.
 +               */
 +              if (refnames->nr == 1)
 +                      error(_("could not delete reference %s: %s"),
 +                            refnames->items[0].string, err.buf);
 +              else
 +                      error(_("could not delete references: %s"), err.buf);
 +
 +              goto out;
 +      }
 +
 +      for (i = 0; i < refnames->nr; i++) {
 +              const char *refname = refnames->items[i].string;
 +
 +              if (delete_ref(refname, NULL, 0))
 +                      result |= error(_("could not remove reference %s"), refname);
 +      }
 +
 +out:
 +      strbuf_release(&err);
 +      return result;
 +}
 +
  /*
   * People using contrib's git-new-workdir have .git/logs/refs ->
   * /some/other/path/.git/logs/refs, and that may live on another device.
@@@ -2986,11 -2910,9 +2986,11 @@@ static int rename_ref_available(const c
        return ret;
  }
  
 -static int write_ref_to_lockfile(struct ref_lock *lock, const unsigned char *sha1);
 +static int write_ref_to_lockfile(struct ref_lock *lock,
 +                               const unsigned char *sha1, struct strbuf *err);
  static int commit_ref_update(struct ref_lock *lock,
 -                           const unsigned char *sha1, const char *logmsg);
 +                           const unsigned char *sha1, const char *logmsg,
 +                           int flags, struct strbuf *err);
  
  int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
  {
        }
        hashcpy(lock->old_oid.hash, orig_sha1);
  
 -      if (write_ref_to_lockfile(lock, orig_sha1) ||
 -          commit_ref_update(lock, orig_sha1, logmsg)) {
 -              error("unable to write current sha1 into %s", newrefname);
 +      if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
 +          commit_ref_update(lock, orig_sha1, logmsg, 0, &err)) {
 +              error("unable to write current sha1 into %s: %s", newrefname, err.buf);
 +              strbuf_release(&err);
                goto rollback;
        }
  
  
        flag = log_all_ref_updates;
        log_all_ref_updates = 0;
 -      if (write_ref_to_lockfile(lock, orig_sha1) ||
 -          commit_ref_update(lock, orig_sha1, NULL))
 -              error("unable to write current sha1 into %s", oldrefname);
 +      if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
 +          commit_ref_update(lock, orig_sha1, NULL, 0, &err)) {
 +              error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
 +              strbuf_release(&err);
 +      }
        log_all_ref_updates = flag;
  
   rollbacklog:
@@@ -3129,23 -3048,8 +3129,23 @@@ static int copy_msg(char *buf, const ch
        return cp - buf;
  }
  
 -/* This function must set a meaningful errno on failure */
 -int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
 +static int should_autocreate_reflog(const char *refname)
 +{
 +      if (!log_all_ref_updates)
 +              return 0;
 +      return starts_with(refname, "refs/heads/") ||
 +              starts_with(refname, "refs/remotes/") ||
 +              starts_with(refname, "refs/notes/") ||
 +              !strcmp(refname, "HEAD");
 +}
 +
 +/*
 + * Create a reflog for a ref.  If force_create = 0, the reflog will
 + * only be created for certain refs (those for which
 + * should_autocreate_reflog returns non-zero.  Otherwise, create it
 + * regardless of the ref name.  Fill in *err and return -1 on failure.
 + */
 +static int log_ref_setup(const char *refname, struct strbuf *sb_logfile, struct strbuf *err, int force_create)
  {
        int logfd, oflags = O_APPEND | O_WRONLY;
        char *logfile;
        logfile = sb_logfile->buf;
        /* make sure the rest of the function can't change "logfile" */
        sb_logfile = NULL;
 -      if (log_all_ref_updates &&
 -          (starts_with(refname, "refs/heads/") ||
 -           starts_with(refname, "refs/remotes/") ||
 -           starts_with(refname, "refs/notes/") ||
 -           !strcmp(refname, "HEAD"))) {
 +      if (force_create || should_autocreate_reflog(refname)) {
                if (safe_create_leading_directories(logfile) < 0) {
 -                      int save_errno = errno;
 -                      error("unable to create directory for %s", logfile);
 -                      errno = save_errno;
 +                      strbuf_addf(err, "unable to create directory for %s: "
 +                                  "%s", logfile, strerror(errno));
                        return -1;
                }
                oflags |= O_CREAT;
  
                if (errno == EISDIR) {
                        if (remove_empty_directories(logfile)) {
 -                              int save_errno = errno;
 -                              error("There are still logs under '%s'",
 -                                    logfile);
 -                              errno = save_errno;
 +                              strbuf_addf(err, "There are still logs under "
 +                                          "'%s'", logfile);
                                return -1;
                        }
                        logfd = open(logfile, oflags, 0666);
                }
  
                if (logfd < 0) {
 -                      int save_errno = errno;
 -                      error("Unable to append to %s: %s", logfile,
 -                            strerror(errno));
 -                      errno = save_errno;
 +                      strbuf_addf(err, "unable to append to %s: %s",
 +                                  logfile, strerror(errno));
                        return -1;
                }
        }
        return 0;
  }
  
 +
 +int safe_create_reflog(const char *refname, int force_create, struct strbuf *err)
 +{
 +      int ret;
 +      struct strbuf sb = STRBUF_INIT;
 +
 +      ret = log_ref_setup(refname, &sb, err, force_create);
 +      strbuf_release(&sb);
 +      return ret;
 +}
 +
  static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
                            const unsigned char *new_sha1,
                            const char *committer, const char *msg)
  
  static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
                           const unsigned char *new_sha1, const char *msg,
 -                         struct strbuf *sb_log_file)
 +                         struct strbuf *sb_log_file, int flags,
 +                         struct strbuf *err)
  {
        int logfd, result, oflags = O_APPEND | O_WRONLY;
        char *log_file;
        if (log_all_ref_updates < 0)
                log_all_ref_updates = !is_bare_repository();
  
 -      result = log_ref_setup(refname, sb_log_file);
 +      result = log_ref_setup(refname, sb_log_file, err, flags & REF_FORCE_CREATE_REFLOG);
 +
        if (result)
                return result;
        log_file = sb_log_file->buf;
        result = log_ref_write_fd(logfd, old_sha1, new_sha1,
                                  git_committer_info(0), msg);
        if (result) {
 -              int save_errno = errno;
 +              strbuf_addf(err, "unable to append to %s: %s", log_file,
 +                          strerror(errno));
                close(logfd);
 -              error("Unable to append to %s", log_file);
 -              errno = save_errno;
                return -1;
        }
        if (close(logfd)) {
 -              int save_errno = errno;
 -              error("Unable to append to %s", log_file);
 -              errno = save_errno;
 +              strbuf_addf(err, "unable to append to %s: %s", log_file,
 +                          strerror(errno));
                return -1;
        }
        return 0;
  }
  
  static int log_ref_write(const char *refname, const unsigned char *old_sha1,
 -                       const unsigned char *new_sha1, const char *msg)
 +                       const unsigned char *new_sha1, const char *msg,
 +                       int flags, struct strbuf *err)
  {
        struct strbuf sb = STRBUF_INIT;
 -      int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb);
 +      int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags,
 +                                err);
        strbuf_release(&sb);
        return ret;
  }
@@@ -3282,36 -3182,36 +3282,36 @@@ int is_branch(const char *refname
  
  /*
   * Write sha1 into the open lockfile, then close the lockfile. On
 - * errors, rollback the lockfile and set errno to reflect the problem.
 + * errors, rollback the lockfile, fill in *err and
 + * return -1.
   */
  static int write_ref_to_lockfile(struct ref_lock *lock,
 -                               const unsigned char *sha1)
 +                               const unsigned char *sha1, struct strbuf *err)
  {
        static char term = '\n';
        struct object *o;
  
        o = parse_object(sha1);
        if (!o) {
 -              error("Trying to write ref %s with nonexistent object %s",
 -                      lock->ref_name, sha1_to_hex(sha1));
 +              strbuf_addf(err,
 +                          "Trying to write ref %s with nonexistent object %s",
 +                          lock->ref_name, sha1_to_hex(sha1));
                unlock_ref(lock);
 -              errno = EINVAL;
                return -1;
        }
        if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
 -              error("Trying to write non-commit object %s to branch %s",
 -                      sha1_to_hex(sha1), lock->ref_name);
 +              strbuf_addf(err,
 +                          "Trying to write non-commit object %s to branch %s",
 +                          sha1_to_hex(sha1), lock->ref_name);
                unlock_ref(lock);
 -              errno = EINVAL;
                return -1;
        }
        if (write_in_full(lock->lk->fd, sha1_to_hex(sha1), 40) != 40 ||
            write_in_full(lock->lk->fd, &term, 1) != 1 ||
            close_ref(lock) < 0) {
 -              int save_errno = errno;
 -              error("Couldn't write %s", lock->lk->filename.buf);
 +              strbuf_addf(err,
 +                          "Couldn't write %s", lock->lk->filename.buf);
                unlock_ref(lock);
 -              errno = save_errno;
                return -1;
        }
        return 0;
   * necessary, using the specified lockmsg (which can be NULL).
   */
  static int commit_ref_update(struct ref_lock *lock,
 -                           const unsigned char *sha1, const char *logmsg)
 +                           const unsigned char *sha1, const char *logmsg,
 +                           int flags, struct strbuf *err)
  {
        clear_loose_ref_cache(&ref_cache);
 -      if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg) < 0 ||
 +      if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0 ||
            (strcmp(lock->ref_name, lock->orig_ref_name) &&
 -           log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg) < 0)) {
 +           log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0)) {
 +              char *old_msg = strbuf_detach(err, NULL);
 +              strbuf_addf(err, "Cannot update the ref '%s': %s",
 +                          lock->ref_name, old_msg);
 +              free(old_msg);
                unlock_ref(lock);
                return -1;
        }
                head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
                                              head_sha1, &head_flag);
                if (head_ref && (head_flag & REF_ISSYMREF) &&
 -                  !strcmp(head_ref, lock->ref_name))
 -                      log_ref_write("HEAD", lock->old_oid.hash, sha1, logmsg);
 +                  !strcmp(head_ref, lock->ref_name)) {
 +                      struct strbuf log_err = STRBUF_INIT;
 +                      if (log_ref_write("HEAD", lock->old_oid.hash, sha1,
 +                                        logmsg, 0, &log_err)) {
 +                              error("%s", log_err.buf);
 +                              strbuf_release(&log_err);
 +                      }
 +              }
        }
        if (commit_ref(lock)) {
                error("Couldn't set %s", lock->ref_name);
                unlock_ref(lock);
                return -1;
        }
 +
        unlock_ref(lock);
        return 0;
  }
@@@ -3383,7 -3271,6 +3383,7 @@@ int create_symref(const char *ref_targe
        int fd, len, written;
        char *git_HEAD = git_pathdup("%s", ref_target);
        unsigned char old_sha1[20], new_sha1[20];
 +      struct strbuf err = STRBUF_INIT;
  
        if (logmsg && read_ref(ref_target, old_sha1))
                hashclr(old_sha1);
  #ifndef NO_SYMLINK_HEAD
        done:
  #endif
 -      if (logmsg && !read_ref(refs_heads_master, new_sha1))
 -              log_ref_write(ref_target, old_sha1, new_sha1, logmsg);
 +      if (logmsg && !read_ref(refs_heads_master, new_sha1) &&
 +              log_ref_write(ref_target, old_sha1, new_sha1, logmsg, 0, &err)) {
 +              error("%s", err.buf);
 +              strbuf_release(&err);
 +      }
  
        free(git_HEAD);
        return 0;
@@@ -3487,14 -3371,14 +3487,14 @@@ static int read_ref_at_ent(unsigned cha
                        hashcpy(cb->sha1, nsha1);
                        if (hashcmp(cb->osha1, nsha1))
                                warning("Log for ref %s has gap after %s.",
 -                                      cb->refname, show_date(cb->date, cb->tz, DATE_RFC2822));
 +                                      cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
                }
                else if (cb->date == cb->at_time)
                        hashcpy(cb->sha1, nsha1);
                else if (hashcmp(nsha1, cb->sha1))
                        warning("Log for ref %s unexpectedly ended on %s.",
                                cb->refname, show_date(cb->date, cb->tz,
 -                                                 DATE_RFC2822));
 +                                                     DATE_MODE(RFC2822)));
                hashcpy(cb->osha1, osha1);
                hashcpy(cb->nsha1, nsha1);
                cb->found_it = 1;
@@@ -4072,19 -3956,14 +4072,19 @@@ int ref_transaction_commit(struct ref_t
                                 * value, so we don't need to write it.
                                 */
                        } else if (write_ref_to_lockfile(update->lock,
 -                                                       update->new_sha1)) {
 +                                                       update->new_sha1,
 +                                                       err)) {
 +                              char *write_err = strbuf_detach(err, NULL);
 +
                                /*
                                 * The lock was freed upon failure of
                                 * write_ref_to_lockfile():
                                 */
                                update->lock = NULL;
 -                              strbuf_addf(err, "cannot update the ref '%s'.",
 -                                          update->refname);
 +                              strbuf_addf(err,
 +                                          "cannot update the ref '%s': %s",
 +                                          update->refname, write_err);
 +                              free(write_err);
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        } else {
  
                if (update->flags & REF_NEEDS_COMMIT) {
                        if (commit_ref_update(update->lock,
 -                                            update->new_sha1, update->msg)) {
 +                                            update->new_sha1, update->msg,
 +                                            update->flags, err)) {
                                /* freed by commit_ref_update(): */
                                update->lock = NULL;
 -                              strbuf_addf(err, "Cannot update the ref '%s'.",
 -                                          update->refname);
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        } else {
@@@ -4158,98 -4038,6 +4158,98 @@@ cleanup
        return ret;
  }
  
 +static int ref_present(const char *refname,
 +                     const struct object_id *oid, int flags, void *cb_data)
 +{
 +      struct string_list *affected_refnames = cb_data;
 +
 +      return string_list_has_string(affected_refnames, refname);
 +}
 +
 +int initial_ref_transaction_commit(struct ref_transaction *transaction,
 +                                 struct strbuf *err)
 +{
 +      struct ref_dir *loose_refs = get_loose_refs(&ref_cache);
 +      struct ref_dir *packed_refs = get_packed_refs(&ref_cache);
 +      int ret = 0, i;
 +      int n = transaction->nr;
 +      struct ref_update **updates = transaction->updates;
 +      struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
 +
 +      assert(err);
 +
 +      if (transaction->state != REF_TRANSACTION_OPEN)
 +              die("BUG: commit called for transaction that is not open");
 +
 +      /* Fail if a refname appears more than once in the transaction: */
 +      for (i = 0; i < n; i++)
 +              string_list_append(&affected_refnames, updates[i]->refname);
 +      string_list_sort(&affected_refnames);
 +      if (ref_update_reject_duplicates(&affected_refnames, err)) {
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +
 +      /*
 +       * It's really undefined to call this function in an active
 +       * repository or when there are existing references: we are
 +       * only locking and changing packed-refs, so (1) any
 +       * simultaneous processes might try to change a reference at
 +       * the same time we do, and (2) any existing loose versions of
 +       * the references that we are setting would have precedence
 +       * over our values. But some remote helpers create the remote
 +       * "HEAD" and "master" branches before calling this function,
 +       * so here we really only check that none of the references
 +       * that we are creating already exists.
 +       */
 +      if (for_each_rawref(ref_present, &affected_refnames))
 +              die("BUG: initial ref transaction called with existing refs");
 +
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if ((update->flags & REF_HAVE_OLD) &&
 +                  !is_null_sha1(update->old_sha1))
 +                      die("BUG: initial ref transaction with old_sha1 set");
 +              if (verify_refname_available(update->refname,
 +                                           &affected_refnames, NULL,
 +                                           loose_refs, err) ||
 +                  verify_refname_available(update->refname,
 +                                           &affected_refnames, NULL,
 +                                           packed_refs, err)) {
 +                      ret = TRANSACTION_NAME_CONFLICT;
 +                      goto cleanup;
 +              }
 +      }
 +
 +      if (lock_packed_refs(0)) {
 +              strbuf_addf(err, "unable to lock packed-refs file: %s",
 +                          strerror(errno));
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if ((update->flags & REF_HAVE_NEW) &&
 +                  !is_null_sha1(update->new_sha1))
 +                      add_packed_ref(update->refname, update->new_sha1);
 +      }
 +
 +      if (commit_packed_refs()) {
 +              strbuf_addf(err, "unable to commit packed-refs file: %s",
 +                          strerror(errno));
 +              ret = TRANSACTION_GENERIC_ERROR;
 +              goto cleanup;
 +      }
 +
 +cleanup:
 +      transaction->state = REF_TRANSACTION_CLOSED;
 +      string_list_clear(&affected_refnames, 0);
 +      return ret;
 +}
 +
  char *shorten_unambiguous_ref(const char *refname, int strict)
  {
        int i;
@@@ -4371,17 -4159,25 +4371,25 @@@ int parse_hide_refs_config(const char *
  
  int ref_is_hidden(const char *refname)
  {
-       struct string_list_item *item;
+       int i;
  
        if (!hide_refs)
                return 0;
-       for_each_string_list_item(item, hide_refs) {
+       for (i = hide_refs->nr - 1; i >= 0; i--) {
+               const char *match = hide_refs->items[i].string;
+               int neg = 0;
                int len;
-               if (!starts_with(refname, item->string))
+               if (*match == '!') {
+                       neg = 1;
+                       match++;
+               }
+               if (!starts_with(refname, match))
                        continue;
-               len = strlen(item->string);
+               len = strlen(match);
                if (!refname[len] || refname[len] == '/')
-                       return 1;
+                       return !neg;
        }
        return 0;
  }