New config push.default to decide default behavior for push
authorFinn Arne Gangstad <finnag@pvv.org>
Mon, 16 Mar 2009 15:42:51 +0000 (16:42 +0100)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 Mar 2009 21:50:21 +0000 (14:50 -0700)
When "git push" is not told what refspecs to push, it pushes all matching
branches to the current remote.  For some workflows this default is not
useful, and surprises new users.  Some have even found that this default
behaviour is too easy to trigger by accident with unwanted consequences.

Introduce a new configuration variable "push.default" that decides what
action git push should take if no refspecs are given or implied by the
command line arguments or the current remote configuration.

Possible values are:

  'nothing'  : Push nothing;
  'matching' : Current default behaviour, push all branches that already
               exist in the current remote;
  'tracking' : Push the current branch to whatever it is tracking;
  'current'  : Push the current branch to a branch of the same name,
               i.e. HEAD.

Signed-off-by: Finn Arne Gangstad <finnag@pvv.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config.txt
builtin-push.c
cache.h
config.c
environment.c

index f5152c5038b49ab5ebe3804e8e9b314d4a752690..7b2b3f44aa5cbd1cf5c17fb7976a1e29d11c8c90 100644 (file)
@@ -1160,6 +1160,24 @@ pull.octopus::
 pull.twohead::
        The default merge strategy to use when pulling a single branch.
 
+push.default::
+       Defines the action git push should take if no refspec is given
+       on the command line, no refspec is configured in the remote, and
+       no refspec is implied by any of the options given on the command
+       line.
++
+The term `current remote` means the remote configured for the current
+branch, or `origin` if no remote is configured. `origin` is also used
+if you are not on any branch. Possible values are:
++
+* `nothing` do not push anything.
+* `matching` push all matching branches to the current remote.
+  All branches having the same name in both ends are considered to be
+  matching. This is the current default value.
+* `tracking` push the current branch to the branch it is tracking.
+* `current` push the current branch to a branch of the same name on the
+  current remote.
+
 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 122fdcfbdc13a11388a091e5ec33d077648fab0a..45fe843b204274cd59db275c1cce9505231174d3 100644 (file)
@@ -48,6 +48,48 @@ static void set_refspecs(const char **refs, int nr)
        }
 }
 
+static void setup_push_tracking(void)
+{
+       struct strbuf refspec = STRBUF_INIT;
+       struct branch *branch = branch_get(NULL);
+       if (!branch)
+               die("You are not currently on a branch.");
+       if (!branch->merge_nr)
+               die("The current branch %s is not tracking anything.",
+                   branch->name);
+       if (branch->merge_nr != 1)
+               die("The current branch %s is tracking multiple branches, "
+                   "refusing to push.", branch->name);
+       strbuf_addf(&refspec, "%s:%s", branch->name, branch->merge[0]->src);
+       add_refspec(refspec.buf);
+}
+
+static void setup_default_push_refspecs(void)
+{
+       git_config(git_default_config, NULL);
+       switch (push_default) {
+       case PUSH_DEFAULT_UNSPECIFIED:
+               /* fallthrough */
+
+       case PUSH_DEFAULT_MATCHING:
+               add_refspec(":");
+               break;
+
+       case PUSH_DEFAULT_TRACKING:
+               setup_push_tracking();
+               break;
+
+       case PUSH_DEFAULT_CURRENT:
+               add_refspec("HEAD");
+               break;
+
+       case PUSH_DEFAULT_NOTHING:
+               die("You didn't specify any refspecs to push, and "
+                   "push.default is \"nothing\".");
+               break;
+       }
+}
+
 static int do_push(const char *repo, int flags)
 {
        int i, errs;
@@ -76,11 +118,12 @@ static int do_push(const char *repo, int flags)
                return error("--all and --mirror are incompatible");
        }
 
-       if (!refspec
-               && !(flags & TRANSPORT_PUSH_ALL)
-               && remote->push_refspec_nr) {
-               refspec = remote->push_refspec;
-               refspec_nr = remote->push_refspec_nr;
+       if (!refspec && !(flags & TRANSPORT_PUSH_ALL)) {
+               if (remote->push_refspec_nr) {
+                       refspec = remote->push_refspec;
+                       refspec_nr = remote->push_refspec_nr;
+               } else if (!(flags & TRANSPORT_PUSH_MIRROR))
+                       setup_default_push_refspecs();
        }
        errs = 0;
        for (i = 0; i < remote->url_nr; i++) {
diff --git a/cache.h b/cache.h
index 189151de25ffd1a6671b7a70f359fa9b94b82173..df4f117ac660aeed05c47bede988f07b13697c18 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -541,8 +541,17 @@ enum rebase_setup_type {
        AUTOREBASE_ALWAYS,
 };
 
+enum push_default_type {
+       PUSH_DEFAULT_UNSPECIFIED = -1,
+       PUSH_DEFAULT_NOTHING = 0,
+       PUSH_DEFAULT_MATCHING,
+       PUSH_DEFAULT_TRACKING,
+       PUSH_DEFAULT_CURRENT,
+};
+
 extern enum branch_track git_branch_track;
 extern enum rebase_setup_type autorebase;
+extern enum push_default_type push_default;
 
 #define GIT_REPO_VERSION 0
 extern int repository_format_version;
index 0c8c76f13b03028ad400b1c5b72b3cf0a7ec0940..30443e32799270c84c6a89c03acae25de83b2476 100644 (file)
--- a/config.c
+++ b/config.c
@@ -565,6 +565,31 @@ static int git_default_branch_config(const char *var, const char *value)
        return 0;
 }
 
+static int git_default_push_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "push.default")) {
+               if (!value)
+                       return config_error_nonbool(var);
+               else if (!strcmp(value, "nothing"))
+                       push_default = PUSH_DEFAULT_NOTHING;
+               else if (!strcmp(value, "matching"))
+                       push_default = PUSH_DEFAULT_MATCHING;
+               else if (!strcmp(value, "tracking"))
+                       push_default = PUSH_DEFAULT_TRACKING;
+               else if (!strcmp(value, "current"))
+                       push_default = PUSH_DEFAULT_CURRENT;
+               else {
+                       error("Malformed value for %s: %s", var, value);
+                       return error("Must be one of nothing, matching, "
+                                    "tracking or current.");
+               }
+               return 0;
+       }
+
+       /* Add other config variables here and to Documentation/config.txt. */
+       return 0;
+}
+
 static int git_default_mailmap_config(const char *var, const char *value)
 {
        if (!strcmp(var, "mailmap.file"))
@@ -588,6 +613,9 @@ int git_default_config(const char *var, const char *value, void *dummy)
        if (!prefixcmp(var, "branch."))
                return git_default_branch_config(var, value);
 
+       if (!prefixcmp(var, "push."))
+               return git_default_push_config(var, value);
+
        if (!prefixcmp(var, "mailmap."))
                return git_default_mailmap_config(var, value);
 
index e278bce0ea5f1ddda2dab9012663c6d1d4c6bd89..4696885b224ac12964fce2eb06617b7ab5633854 100644 (file)
@@ -42,6 +42,7 @@ enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
 enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
 enum rebase_setup_type autorebase = AUTOREBASE_NEVER;
+enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;
 
 /* Parallel index stat data preload? */
 int core_preload_index = 0;