Merge branch 'jk/push-force-with-lease-creation'
authorJunio C Hamano <gitster@pobox.com>
Wed, 10 Aug 2016 19:33:18 +0000 (12:33 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 10 Aug 2016 19:33:18 +0000 (12:33 -0700)
"git push --force-with-lease" already had enough logic to allow
ensuring that such a push results in creation of a ref (i.e. the
receiving end did not have another push from sideways that would be
discarded by our force-pushing), but didn't expose this possibility
to the users. It does so now.

* jk/push-force-with-lease-creation:
t5533: make it pass on case-sensitive filesystems
push: allow pushing new branches with --force-with-lease
push: add shorthand for --force-with-lease branch creation
Documentation/git-push: fix placeholder formatting

1  2 
Documentation/git-push.txt
remote.c
remote.h
index ec514f6cd5cc0f019b469d91fc6af365ce0a64a9,02b5dd3490c38d99f77a195e3024b644c32a434f..47b77e693bca1675a4887bddb5147077d60805a0
@@@ -10,8 -10,8 +10,8 @@@ SYNOPSI
  --------
  [verse]
  'git push' [--all | --mirror | --tags] [--follow-tags] [--atomic] [-n | --dry-run] [--receive-pack=<git-receive-pack>]
 -         [--repo=<repository>] [-f | --force] [--prune] [-v | --verbose]
 -         [-u | --set-upstream]
 +         [--repo=<repository>] [-f | --force] [-d | --delete] [--prune] [-v | --verbose]
 +         [-u | --set-upstream] [--push-option=<string>]
           [--[no-]signed|--sign=(true|false|if-asked)]
           [--force-with-lease[=<refname>[:<expect>]]]
           [--no-verify] [<repository> [<refspec>...]]
@@@ -137,8 -137,8 +137,8 @@@ already exists on the remote side
        and also push annotated tags in `refs/tags` that are missing
        from the remote but are pointing at commit-ish that are
        reachable from the refs being pushed.  This can also be specified
 -      with configuration variable 'push.followTags'.  For more
 -      information, see 'push.followTags' in linkgit:git-config[1].
 +      with configuration variable `push.followTags`.  For more
 +      information, see `push.followTags` in linkgit:git-config[1].
  
  --[no-]signed::
  --sign=(true|false|if-asked)::
        Either all refs are updated, or on error, no refs are updated.
        If the server does not support atomic pushes the push will fail.
  
 +-o::
 +--push-option::
 +      Transmit the given string to the server, which passes them to
 +      the pre-receive as well as the post-receive hook. The given string
 +      must not contain a NUL or LF character.
 +
  --receive-pack=<git-receive-pack>::
  --exec=<git-receive-pack>::
        Path to the 'git-receive-pack' program on the remote
@@@ -204,10 -198,11 +204,11 @@@ branch we have for it
  +
  `--force-with-lease=<refname>:<expect>` will protect the named ref (alone),
  if it is going to be updated, by requiring its current value to be
- the same as the specified value <expect> (which is allowed to be
+ the same as the specified value `<expect>` (which is allowed to be
  different from the remote-tracking branch we have for the refname,
  or we do not even have to have such a remote-tracking branch when
- this form is used).
+ this form is used).  If `<expect>` is the empty string, then the named ref
+ must not already exist.
  +
  Note that all forms other than `--force-with-lease=<refname>:<expect>`
  that specifies the expected current value of the ref explicitly are
@@@ -246,7 -241,7 +247,7 @@@ origin +master` to force a push to the 
        For every branch that is up to date or successfully pushed, add
        upstream (tracking) reference, used by argument-less
        linkgit:git-pull[1] and other commands. For more information,
 -      see 'branch.<name>.merge' in linkgit:git-config[1].
 +      see `branch.<name>.merge` in linkgit:git-config[1].
  
  --[no-]thin::
        These options are passed to linkgit:git-send-pack[1]. A thin transfer
        all submodules that changed in the revisions to be pushed will be
        pushed. If on-demand was not able to push all necessary revisions
        it will also be aborted and exit with non-zero status. A value of
 -      'no' or using '--no-recurse-submodules' can be used to override the
 +      'no' or using `--no-recurse-submodules` can be used to override the
        push.recurseSubmodules configuration variable when no submodule
        recursion is required.
  
        default is --verify, giving the hook a chance to prevent the
        push.  With --no-verify, the hook is bypassed completely.
  
 +-4::
 +--ipv4::
 +      Use IPv4 addresses only, ignoring IPv6 addresses.
 +
 +-6::
 +--ipv6::
 +      Use IPv6 addresses only, ignoring IPv4 addresses.
  
  include::urls-remotes.txt[]
  
diff --combined remote.c
index a326e4e2516e2129e7a08bfc149786402ec160fb,3b6f4b7e6d18960ba6ffa72a8dc7803d5377a140..d29850a81cdb09b41da5dc3e7284106672d85a4c
+++ b/remote.c
@@@ -256,7 -256,7 +256,7 @@@ static void read_remotes_file(struct re
        if (!f)
                return;
        remote->origin = REMOTE_REMOTES;
 -      while (strbuf_getline(&buf, f, '\n') != EOF) {
 +      while (strbuf_getline(&buf, f) != EOF) {
                const char *v;
  
                strbuf_rtrim(&buf);
@@@ -281,7 -281,7 +281,7 @@@ static void read_branches_file(struct r
        if (!f)
                return;
  
 -      strbuf_getline(&buf, f, '\n');
 +      strbuf_getline_lf(&buf, f);
        fclose(f);
        strbuf_trim(&buf);
        if (!buf.len) {
  static int handle_config(const char *key, const char *value, void *cb)
  {
        const char *name;
 +      int namelen;
        const char *subkey;
        struct remote *remote;
        struct branch *branch;
 -      if (starts_with(key, "branch.")) {
 -              name = key + 7;
 -              subkey = strrchr(name, '.');
 -              if (!subkey)
 +      if (parse_config_key(key, "branch", &name, &namelen, &subkey) >= 0) {
 +              if (!name)
                        return 0;
 -              branch = make_branch(name, subkey - name);
 -              if (!strcmp(subkey, ".remote")) {
 +              branch = make_branch(name, namelen);
 +              if (!strcmp(subkey, "remote")) {
                        return git_config_string(&branch->remote_name, key, value);
 -              } else if (!strcmp(subkey, ".pushremote")) {
 +              } else if (!strcmp(subkey, "pushremote")) {
                        return git_config_string(&branch->pushremote_name, key, value);
 -              } else if (!strcmp(subkey, ".merge")) {
 +              } else if (!strcmp(subkey, "merge")) {
                        if (!value)
                                return config_error_nonbool(key);
                        add_merge(branch, xstrdup(value));
                }
                return 0;
        }
 -      if (starts_with(key, "url.")) {
 +      if (parse_config_key(key, "url", &name, &namelen, &subkey) >= 0) {
                struct rewrite *rewrite;
 -              name = key + 4;
 -              subkey = strrchr(name, '.');
 -              if (!subkey)
 +              if (!name)
                        return 0;
 -              if (!strcmp(subkey, ".insteadof")) {
 -                      rewrite = make_rewrite(&rewrites, name, subkey - name);
 +              if (!strcmp(subkey, "insteadof")) {
 +                      rewrite = make_rewrite(&rewrites, name, namelen);
                        if (!value)
                                return config_error_nonbool(key);
                        add_instead_of(rewrite, xstrdup(value));
 -              } else if (!strcmp(subkey, ".pushinsteadof")) {
 -                      rewrite = make_rewrite(&rewrites_push, name, subkey - name);
 +              } else if (!strcmp(subkey, "pushinsteadof")) {
 +                      rewrite = make_rewrite(&rewrites_push, name, namelen);
                        if (!value)
                                return config_error_nonbool(key);
                        add_instead_of(rewrite, xstrdup(value));
                }
        }
  
 -      if (!starts_with(key,  "remote."))
 +      if (parse_config_key(key, "remote", &name, &namelen, &subkey) < 0)
                return 0;
 -      name = key + 7;
  
        /* Handle remote.* variables */
 -      if (!strcmp(name, "pushdefault"))
 +      if (!name && !strcmp(subkey, "pushdefault"))
                return git_config_string(&pushremote_name, key, value);
  
 +      if (!name)
 +              return 0;
        /* Handle remote.<name>.* variables */
        if (*name == '/') {
                warning("Config remote shorthand cannot begin with '/': %s",
                        name);
                return 0;
        }
 -      subkey = strrchr(name, '.');
 -      if (!subkey)
 -              return 0;
 -      remote = make_remote(name, subkey - name);
 +      remote = make_remote(name, namelen);
        remote->origin = REMOTE_CONFIG;
 -      if (!strcmp(subkey, ".mirror"))
 +      if (!strcmp(subkey, "mirror"))
                remote->mirror = git_config_bool(key, value);
 -      else if (!strcmp(subkey, ".skipdefaultupdate"))
 +      else if (!strcmp(subkey, "skipdefaultupdate"))
                remote->skip_default_update = git_config_bool(key, value);
 -      else if (!strcmp(subkey, ".skipfetchall"))
 +      else if (!strcmp(subkey, "skipfetchall"))
                remote->skip_default_update = git_config_bool(key, value);
 -      else if (!strcmp(subkey, ".prune"))
 +      else if (!strcmp(subkey, "prune"))
                remote->prune = git_config_bool(key, value);
 -      else if (!strcmp(subkey, ".url")) {
 +      else if (!strcmp(subkey, "url")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                add_url(remote, v);
 -      } else if (!strcmp(subkey, ".pushurl")) {
 +      } else if (!strcmp(subkey, "pushurl")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                add_pushurl(remote, v);
 -      } else if (!strcmp(subkey, ".push")) {
 +      } else if (!strcmp(subkey, "push")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                add_push_refspec(remote, v);
 -      } else if (!strcmp(subkey, ".fetch")) {
 +      } else if (!strcmp(subkey, "fetch")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                add_fetch_refspec(remote, v);
 -      } else if (!strcmp(subkey, ".receivepack")) {
 +      } else if (!strcmp(subkey, "receivepack")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                        remote->receivepack = v;
                else
                        error("more than one receivepack given, using the first");
 -      } else if (!strcmp(subkey, ".uploadpack")) {
 +      } else if (!strcmp(subkey, "uploadpack")) {
                const char *v;
                if (git_config_string(&v, key, value))
                        return -1;
                        remote->uploadpack = v;
                else
                        error("more than one uploadpack given, using the first");
 -      } else if (!strcmp(subkey, ".tagopt")) {
 +      } else if (!strcmp(subkey, "tagopt")) {
                if (!strcmp(value, "--no-tags"))
                        remote->fetch_tags = -1;
                else if (!strcmp(value, "--tags"))
                        remote->fetch_tags = 2;
 -      } else if (!strcmp(subkey, ".proxy")) {
 +      } else if (!strcmp(subkey, "proxy")) {
                return git_config_string((const char **)&remote->http_proxy,
                                         key, value);
 -      } else if (!strcmp(subkey, ".vcs")) {
 +      } else if (!strcmp(subkey, "proxyauthmethod")) {
 +              return git_config_string((const char **)&remote->http_proxy_authmethod,
 +                                       key, value);
 +      } else if (!strcmp(subkey, "vcs")) {
                return git_config_string(&remote->foreign_vcs, key, value);
        }
        return 0;
@@@ -455,6 -457,7 +455,6 @@@ static void read_config(void
  {
        static int loaded;
        struct object_id oid;
 -      const char *head_ref;
        int flag;
  
        if (loaded)
        loaded = 1;
  
        current_branch = NULL;
 -      head_ref = resolve_ref_unsafe("HEAD", 0, oid.hash, &flag);
 -      if (head_ref && (flag & REF_ISSYMREF) &&
 -          skip_prefix(head_ref, "refs/heads/", &head_ref)) {
 -              current_branch = make_branch(head_ref, 0);
 +      if (startup_info->have_repository) {
 +              const char *head_ref = resolve_ref_unsafe("HEAD", 0, oid.hash, &flag);
 +              if (head_ref && (flag & REF_ISSYMREF) &&
 +                  skip_prefix(head_ref, "refs/heads/", &head_ref)) {
 +                      current_branch = make_branch(head_ref, 0);
 +              }
        }
        git_config(handle_config, NULL);
        alias_all_urls();
@@@ -714,9 -715,18 +714,9 @@@ struct remote *pushremote_get(const cha
        return remote_get_1(name, pushremote_for_branch);
  }
  
 -int remote_is_configured(const char *name)
 +int remote_is_configured(struct remote *remote)
  {
 -      struct remotes_hash_key lookup;
 -      struct hashmap_entry lookup_entry;
 -      read_config();
 -
 -      init_remotes_hash();
 -      lookup.str = name;
 -      lookup.len = strlen(name);
 -      hashmap_entry_init(&lookup_entry, memhash(name, lookup.len));
 -
 -      return hashmap_get(&remotes_hash, &lookup_entry, &lookup) != NULL;
 +      return remote && remote->origin;
  }
  
  int for_each_remote(each_remote_fn fn, void *priv)
@@@ -1544,8 -1554,7 +1544,7 @@@ void set_ref_status_for_push(struct re
                 * branch.
                 */
                if (ref->expect_old_sha1) {
-                       if (ref->expect_old_no_trackback ||
-                           oidcmp(&ref->old_oid, &ref->old_oid_expect))
+                       if (oidcmp(&ref->old_oid, &ref->old_oid_expect))
                                reject_reason = REF_STATUS_REJECT_STALE;
                        else
                                /* If the ref isn't stale then force the update. */
@@@ -1660,7 -1669,7 +1659,7 @@@ int branch_merge_matches(struct branch 
        return refname_match(branch->merge[i]->src, refname);
  }
  
 -__attribute((format (printf,2,3)))
 +__attribute__((format (printf,2,3)))
  static const char *error_buf(struct strbuf *err, const char *fmt, ...)
  {
        if (err) {
@@@ -2108,7 -2117,7 +2107,7 @@@ int format_tracking_info(struct branch 
                           "Your branch and '%s' have diverged,\n"
                               "and have %d and %d different commits each, "
                               "respectively.\n",
 -                         theirs),
 +                         ours + theirs),
                        base, ours, theirs);
                if (advice_status_hints)
                        strbuf_addf(sb,
@@@ -2294,6 -2303,8 +2293,8 @@@ int parse_push_cas_option(struct push_c
        entry = add_cas_entry(cas, arg, colon - arg);
        if (!*colon)
                entry->use_tracking = 1;
+       else if (!colon[1])
+               hashclr(entry->expect);
        else if (get_sha1(colon + 1, entry->expect))
                return error("cannot parse expected object name '%s'", colon + 1);
        return 0;
@@@ -2343,7 -2354,7 +2344,7 @@@ static void apply_cas(struct push_cas_o
                if (!entry->use_tracking)
                        hashcpy(ref->old_oid_expect.hash, cas->entry[i].expect);
                else if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
-                       ref->expect_old_no_trackback = 1;
+                       oidclr(&ref->old_oid_expect);
                return;
        }
  
  
        ref->expect_old_sha1 = 1;
        if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
-               ref->expect_old_no_trackback = 1;
+               oidclr(&ref->old_oid_expect);
  }
  
  void apply_push_cas(struct push_cas_option *cas,
diff --combined remote.h
index c21fd3788c78f28d57e7d76d472dc411986b34d1,9674ddb0afe1f944c9ccf5e3ca1c782eafe9bdc8..924881169d9f6c5b9b09e2434d964f62e0e28d09
+++ b/remote.h
@@@ -5,7 -5,6 +5,7 @@@
  #include "hashmap.h"
  
  enum {
 +      REMOTE_UNCONFIGURED = 0,
        REMOTE_CONFIG,
        REMOTE_REMOTES,
        REMOTE_BRANCHES
@@@ -55,12 -54,11 +55,12 @@@ struct remote 
         * for curl remotes only
         */
        char *http_proxy;
 +      char *http_proxy_authmethod;
  };
  
  struct remote *remote_get(const char *name);
  struct remote *pushremote_get(const char *name);
 -int remote_is_configured(const char *name);
 +int remote_is_configured(struct remote *remote);
  
  typedef int each_remote_fn(struct remote *remote, void *priv);
  int for_each_remote(each_remote_fn fn, void *priv);
@@@ -89,7 -87,6 +89,6 @@@ struct ref 
                force:1,
                forced_update:1,
                expect_old_sha1:1,
-               expect_old_no_trackback:1,
                deletion:1,
                matched:1;