{"thread":{"id":"20170","subject":"[PATCH RFC 0/8] introduce 'git remote add --push' and 'git clone --push'","startedAt":"2009-07-20T17:49:47Z","lastAt":"2009-07-21T21:00:20Z","messageCount":16,"participants":["Paolo Bonzini","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"118333","messageId":"1248112195-3761-1-git-send-email-bonzini@gnu.org","threadId":"20170","inReplyTo":null,"subject":"[PATCH RFC 0/8] introduce 'git remote add --push' and 'git clone --push'","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-20T17:49:47Z","receivedAt":"2009-07-20T17:49:47Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"I post as an initial RFC the third series in the push.default saga.\nThis is on top of origin/next and even then it requires the\n\"push --current\" patches to be fully functional.  Even without it,\nhowever, it will create correct configuration.\n\nThe series adds --push options to \"git remote add\" and \"git clone\".\nThese accept a push strategy of the same kind as \"push.default\",\nand will use it to create push configuration and refspecs.  These\nwill then override push.default.\n\nThe argument is optional, in which case it will be taken from the\n\"push.default\" config key itself.  In this case, since the user explicitly\nrequested something they did not configure, a warning is emitted similar\nto the one of 1.6.3.  If --push is used appropriately, in a non-specified\nfuture \"git push\" might not use push.default anymore and thus require\na push refspec.  (Or more realistically, it would look at push.default,\nbut only to suggest the user commands that do the transition).\n\nThis patch is a bit harder to justify than the others so far, since\nit may as well look like a feature in search of a use case.  To this,\nmy reply is that this is just a step towards a more polished (IMO)\nimplementation of the existing \"push.default\" feature.  Actually, the\npatch is big but it is mostly builtin-remote.c and tests.  And you\nwill have to put up with this for only another series or two.\n\nPatch 1 is just taken from the push --current series.  Patch 2 adds\na new value for `push.default', so that it's easier to map --mirror\nto a --push suboption.\n\nPatches 3 and 4 add the option to \"git remote add\" (refactoring\nfirst, adding the feature later).\n\nPatches 5 to 8 add the option to \"git clone\".  The first two share\nthe code for the config setup between \"git remote add\" and \"git clone\".\nThe third adds a new function to get just the global configuration\n(not the repository one), for usage in \"git clone\" before the repository\nis actually created.  The final one finally adds the new option,\nwhich is little more than tweaking the call into \"git remote add\".\n\nThat's it for now.  I'll post the final version of this series when I\nget comments *and* once both autosetuppush and push --current have been\ncommitted to next.  In the meanwhile, opinions, reviews and flames are\nwelcome as usual.\n\n\nPaolo Bonzini (8):\n      reintroduce PUSH_DEFAULT_UNSPECIFIED\n      push: add push.default = mirror\n      git remote add: refactor configuration\n      git remote add: add --push option\n      clone: refactoring of building the fetch refspec\n      clone: use setup_remote_config\n      config: add git_config_norepo\n      clone: add --push option\n\n Documentation/config.txt     |    2 +\n Documentation/git-clone.txt  |   13 +++-\n Documentation/git-remote.txt |   13 ++--\n builtin-clone.c              |  133 +++++++++++++++---------------\n builtin-push.c               |   13 +++-\n builtin-remote.c             |  188 ++++++++++++++++++++++++++++++++++--------\n cache.h                      |    5 +\n config.c                     |   74 +++++++++++-----\n environment.c                |    2 +-\n remote.h                     |    3 +\n t/t5505-remote.sh            |   73 ++++++++++++++++\n t/t5517-push-mirror.sh       |   22 +++++-\n t/t5601-clone.sh             |   78 +++++++++++++++++\n 13 files changed, 486 insertions(+), 133 deletions(-)\n"},{"id":"118334","messageId":"1248112195-3761-2-git-send-email-bonzini@gnu.org","threadId":"20170","inReplyTo":"1248112195-3761-1-git-send-email-bonzini@gnu.org","subject":"[PATCH RFC 1/8] reintroduce PUSH_DEFAULT_UNSPECIFIED","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-20T17:49:48Z","receivedAt":"2009-07-20T17:49:48Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This is just the first patch from the push --current series.  It\nis the only one needed as a prerequisite for these patches, at\nleast to get as far as writing the configuration in .git/config.\n\nNot-signed-off-for-this-rfc-by: Paolo Bonzini <bonzini@gnu.org>\n---\n builtin-push.c |    1 +\n cache.h        |    1 +\n environment.c  |    2 +-\n 3 files changed, 3 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-push.c b/builtin-push.c\nindex 1d92e22..e678a9d 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -69,6 +69,7 @@ static void setup_default_push_refspecs(void)\n \tgit_config(git_default_config, NULL);\n \tswitch (push_default) {\n \tdefault:\n+\tcase PUSH_DEFAULT_UNSPECIFIED:\n \tcase PUSH_DEFAULT_MATCHING:\n \t\tadd_refspec(\":\");\n \t\tbreak;\ndiff --git a/cache.h b/cache.h\nindex dbe460c..f10513f 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -544,6 +544,7 @@ enum rebase_setup_type {\n };\n \n enum push_default_type {\n+\tPUSH_DEFAULT_UNSPECIFIED = -1,\n \tPUSH_DEFAULT_NOTHING = 0,\n \tPUSH_DEFAULT_MATCHING,\n \tPUSH_DEFAULT_TRACKING,\ndiff --git a/environment.c b/environment.c\nindex 95aa8a6..4b68109 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -46,7 +46,7 @@ struct tracking_config git_branch_track = {\n \tAUTOREBASE_NEVER,\n \t0\n };\n-enum push_default_type push_default = PUSH_DEFAULT_MATCHING;\n+enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #ifndef OBJECT_CREATION_MODE\n #define OBJECT_CREATION_MODE OBJECT_CREATION_USES_HARDLINKS\n #endif\n-- \n1.6.2.5\n"},{"id":"118335","messageId":"1248112195-3761-3-git-send-email-bonzini@gnu.org","threadId":"20170","inReplyTo":"1248112195-3761-1-git-send-email-bonzini@gnu.org","subject":"[PATCH RFC 2/8] push: add push.default = mirror","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-20T17:49:49Z","receivedAt":"2009-07-20T17:49:49Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This patch adds a new value for push.default.  The aim of the series is\nto support all push.default values as arguments to `--push' in git-clone\nand git-remote, and if push.default=mirror works it is easy to make\n`--mirror' a synonym for `--push=mirror' in those comments.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n Documentation/config.txt |    2 ++\n builtin-push.c           |   12 ++++++++++--\n cache.h                  |    1 +\n config.c                 |    4 +++-\n 4 files changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 4c27e9d..fa5eb76 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1290,6 +1290,8 @@ push.default::\n * `matching` push all matching branches.\n   All branches having the same name in both ends are considered to be\n   matching. This is the default.\n+* `mirror` pushes all branches forcing non fast-forward updates and\n+  deletes branches that do not exist anymore locally.\n * `tracking` push the current branch to its upstream branch.\n * `current` push the current branch to a branch of the same name.\n \ndiff --git a/builtin-push.c b/builtin-push.c\nindex e678a9d..8a312a3 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -66,7 +66,6 @@ static void setup_push_tracking(void)\n \n static void setup_default_push_refspecs(void)\n {\n-\tgit_config(git_default_config, NULL);\n \tswitch (push_default) {\n \tdefault:\n \tcase PUSH_DEFAULT_UNSPECIFIED:\n@@ -74,6 +73,10 @@ static void setup_default_push_refspecs(void)\n \t\tadd_refspec(\":\");\n \t\tbreak;\n \n+\tcase PUSH_DEFAULT_MIRROR:\n+\t\tadd_refspec(\"+refs/*:refs/*\");\n+\t\tbreak;\n+\n \tcase PUSH_DEFAULT_TRACKING:\n \t\tsetup_push_tracking();\n \t\tbreak;\n@@ -126,8 +129,12 @@ static int do_push(const char *repo, int flags)\n \t\tif (remote->push_refspec_nr) {\n \t\t\trefspec = remote->push_refspec;\n \t\t\trefspec_nr = remote->push_refspec_nr;\n-\t\t} else if (!(flags & TRANSPORT_PUSH_MIRROR))\n+\t\t} else if (!(flags & TRANSPORT_PUSH_MIRROR)\n+\t\t\t   || push_default == PUSH_DEFAULT_MIRROR) {\n+\t\t\tif (push_default == PUSH_DEFAULT_MIRROR)\n+\t\t\t\tflags |= TRANSPORT_PUSH_MIRROR;\n \t\t\tsetup_default_push_refspecs();\n+\t\t}\n \t}\n \terrs = 0;\n \tif (remote->pushurl_nr) {\n@@ -184,6 +191,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n+\tgit_config(git_default_config, NULL);\n \targc = parse_options(argc, argv, prefix, options, push_usage, 0);\n \n \tif (tags)\ndiff --git a/cache.h b/cache.h\nindex f10513f..172d36c 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -549,6 +549,7 @@ enum push_default_type {\n \tPUSH_DEFAULT_MATCHING,\n \tPUSH_DEFAULT_TRACKING,\n \tPUSH_DEFAULT_CURRENT,\n+\tPUSH_DEFAULT_MIRROR,\n };\n \n struct tracking_config {\ndiff --git a/config.c b/config.c\nindex 04380bb..4db5c6d 100644\n--- a/config.c\n+++ b/config.c\n@@ -604,6 +604,8 @@ static int git_default_push_config(const char *var, const char *value)\n \t\t\tpush_default = PUSH_DEFAULT_NOTHING;\n \t\telse if (!strcmp(value, \"matching\"))\n \t\t\tpush_default = PUSH_DEFAULT_MATCHING;\n+\t\telse if (!strcmp(value, \"mirror\"))\n+\t\t\tpush_default = PUSH_DEFAULT_MIRROR;\n \t\telse if (!strcmp(value, \"tracking\"))\n \t\t\tpush_default = PUSH_DEFAULT_TRACKING;\n \t\telse if (!strcmp(value, \"current\"))\n@@ -611,7 +613,7 @@ static int git_default_push_config(const char *var, const char *value)\n \t\telse {\n \t\t\terror(\"Malformed value for %s: %s\", var, value);\n \t\t\treturn error(\"Must be one of nothing, matching, \"\n-\t\t\t\t     \"tracking or current.\");\n+\t\t\t\t     \"mirror, tracking or current.\");\n \t\t}\n \t\treturn 0;\n \t}\n-- \n1.6.2.5\n"},{"id":"118336","messageId":"1248112195-3761-4-git-send-email-bonzini@gnu.org","threadId":"20170","inReplyTo":"1248112195-3761-1-git-send-email-bonzini@gnu.org","subject":"[PATCH RFC 3/8] git remote add: refactor configuration","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-20T17:49:50Z","receivedAt":"2009-07-20T17:49:50Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This moves the configuration setup of git remote add to\na separate function.  The next patch will add more\nconfiguration options in setup_remote_config.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n builtin-remote.c |   74 ++++++++++++++++++++++++++++++++----------------------\n 1 files changed, 44 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin-remote.c b/builtin-remote.c\nindex 008abfe..c30fbb7 100644\n--- a/builtin-remote.c\n+++ b/builtin-remote.c\n@@ -36,6 +36,49 @@ static inline int postfixcmp(const char *string, const char *postfix)\n \treturn strcmp(string + len1 - len2, postfix);\n }\n \n+static int setup_remote_config(const char *name, const char *url, int mirror, struct string_list *track)\n+{\n+\tstruct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;\n+\tint i;\n+\n+\tif (mirror) {\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addf(&buf, \"remote.%s.mirror\", name);\n+\t\tif (git_config_set(buf.buf, \"true\"))\n+\t\t\treturn 1;\n+\t}\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"remote.%s.url\", name);\n+\tif (git_config_set(buf.buf, url))\n+\t\treturn 1;\n+\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addf(&buf, \"remote.%s.fetch\", name);\n+\n+\tif (track->nr == 0)\n+\t\tstring_list_append(\"*\", track);\n+\n+\tfor (i = 0; i < track->nr; i++) {\n+\t\tstruct string_list_item *item = track->items + i;\n+\n+\t\tstrbuf_reset(&buf2);\n+\t\tif (mirror)\n+\t\t\tstrbuf_addf(&buf2, \"+refs/%s:refs/%s\",\n+\t\t\t\t\titem->string, item->string);\n+\t\telse\n+\t\t\tstrbuf_addf(&buf2, \"+refs/heads/%s:refs/remotes/%s/%s\",\n+\t\t\t\t\titem->string, name, item->string);\n+\t\tif (git_config_set_multivar(buf.buf, buf2.buf, \"^$\", 0))\n+\t\t\treturn 1;\n+\t}\n+\n+\tstrbuf_release(&buf);\n+\tstrbuf_release(&buf2);\n+\treturn 0;\n+}\n+\n+\n static int opt_parse_track(const struct option *opt, const char *arg, int not)\n {\n \tstruct string_list *list = opt->value;\n@@ -67,7 +110,6 @@ static int add(int argc, const char **argv)\n \tstruct remote *remote;\n \tstruct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;\n \tconst char *name, *url;\n-\tint i;\n \n \tstruct option options[] = {\n \t\tOPT_GROUP(\"add specific options\"),\n@@ -97,37 +139,9 @@ static int add(int argc, const char **argv)\n \tif (!valid_fetch_refspec(buf2.buf))\n \t\tdie(\"'%s' is not a valid remote name\", name);\n \n-\tstrbuf_addf(&buf, \"remote.%s.url\", name);\n-\tif (git_config_set(buf.buf, url))\n+\tif (setup_remote_config(name, url, mirror, &track))\n \t\treturn 1;\n \n-\tstrbuf_reset(&buf);\n-\tstrbuf_addf(&buf, \"remote.%s.fetch\", name);\n-\n-\tif (track.nr == 0)\n-\t\tstring_list_append(\"*\", &track);\n-\tfor (i = 0; i < track.nr; i++) {\n-\t\tstruct string_list_item *item = track.items + i;\n-\n-\t\tstrbuf_reset(&buf2);\n-\t\tstrbuf_addch(&buf2, '+');\n-\t\tif (mirror)\n-\t\t\tstrbuf_addf(&buf2, \"refs/%s:refs/%s\",\n-\t\t\t\t\titem->string, item->string);\n-\t\telse\n-\t\t\tstrbuf_addf(&buf2, \"refs/heads/%s:refs/remotes/%s/%s\",\n-\t\t\t\t\titem->string, name, item->string);\n-\t\tif (git_config_set_multivar(buf.buf, buf2.buf, \"^$\", 0))\n-\t\t\treturn 1;\n-\t}\n-\n-\tif (mirror) {\n-\t\tstrbuf_reset(&buf);\n-\t\tstrbuf_addf(&buf, \"remote.%s.mirror\", name);\n-\t\tif (git_config_set(buf.buf, \"true\"))\n-\t\t\treturn 1;\n-\t}\n-\n \tif (fetch && fetch_remote(name))\n \t\treturn 1;\n \n-- \n1.6.2.5\n"},{"id":"118340","messageId":"1248112195-3761-5-git-send-email-bonzini@gnu.org","threadId":"20170","inReplyTo":"1248112195-3761-1-git-send-email-bonzini@gnu.org","subject":"[PATCH RFC 4/8] git remote add: add --push option","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-20T17:49:51Z","receivedAt":"2009-07-20T17:49:51Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This patch makes `git remote add' able to setup push refspecs,\nas well as options controlling the behavior of git-push.  Remotes\ncreated with this option will not be subject to the \"push.default\"\nconfiguration setting, because they have a push refspec.\n\nThis plans ahead for a future transition to \"push.default = nothing\"\nbeing the default, while being a worthwhile addition in case the\ntransition never materializes.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n Documentation/git-remote.txt |   13 +++--\n builtin-remote.c             |  119 +++++++++++++++++++++++++++++++++++++++--\n cache.h                      |    4 +-\n config.c                     |   39 ++++++++------\n t/t5505-remote.sh            |   73 ++++++++++++++++++++++++++\n t/t5517-push-mirror.sh       |   22 +++++++-\n 6 files changed, 240 insertions(+), 30 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 9e2b4ea..5f1bda3 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -10,7 +10,7 @@ SYNOPSIS\n --------\n [verse]\n 'git remote' [-v | --verbose]\n-'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>\n+'git remote add' [-t <branch>] [-m <master>] [-f] [--mirror | --push=<strategy>] <name> <url>\n 'git remote rename' <old> <new>\n 'git remote rm' <name>\n 'git remote set-head' <name> [-a | -d | <branch>]\n@@ -56,11 +56,12 @@ multiple branches without grabbing all branches.\n With `-m <master>` option, `$GIT_DIR/remotes/<name>/HEAD` is set\n up to point at remote's `<master>` branch. See also the set-head command.\n +\n-In mirror mode, enabled with `\\--mirror`, the refs will not be stored\n-in the 'refs/remotes/' namespace, but in 'refs/heads/'.  This option\n-only makes sense in bare repositories.  If a remote uses mirror\n-mode, furthermore, `git push` will always behave as if `\\--mirror`\n-was passed.\n+The remote's behavior upon `git push` can also be set up by\n+`git remote add`.  Valid values for `\\--push` are 'matching', 'mirror,\n+'tracking', 'current', and `nothing'.  Their meanings are the same as\n+for the `push.default` configuration key.  `\\--mirror` is a synonym for\n+`\\--push=mirror`.\n+\n \n 'rename'::\n \ndiff --git a/builtin-remote.c b/builtin-remote.c\nindex c30fbb7..23ab24b 100644\n--- a/builtin-remote.c\n+++ b/builtin-remote.c\n@@ -9,7 +9,7 @@\n \n static const char * const builtin_remote_usage[] = {\n \t\"git remote [-v | --verbose]\",\n-\t\"git remote add [-t <branch>] [-m <master>] [-f] [--mirror] <name> <url>\",\n+\t\"git remote add [-t <branch>] [-m <master>] [-f] [--push=<strategy>] [--mirror] <name> <url>\",\n \t\"git remote rename <old> <new>\",\n \t\"git remote rm <name>\",\n \t\"git remote set-head <name> [-a | -d | <branch>]\",\n@@ -36,17 +36,92 @@ static inline int postfixcmp(const char *string, const char *postfix)\n \treturn strcmp(string + len1 - len2, postfix);\n }\n \n-static int setup_remote_config(const char *name, const char *url, int mirror, struct string_list *track)\n+static const char *warn_unconfigured_push_msg[] = {\n+\t\"You did not specify any argument to --push, and 'push.default'\",\n+\t\"is not defined in your configuration. The default action in this\",\n+\t\"case will be to push all matching refspecs, that is, all branches\",\n+\t\"that exist both locally and remotely will be updated.  This may\",\n+\t\"not necessarily be what you want to happen.\",\n+\t\"\",\n+\t\"You can specify what action you want to take in this case, and\",\n+\t\"avoid seeing this message again, by configuring 'push.default' to:\",\n+\t\"  'matching' : Push all matching branches (default)\",\n+\t\"  'mirror'   : Push all branches and delete non-existing ones\",\n+\t\"  'tracking' : Push the current branch to whatever it is tracking\",\n+\t\"  'current'  : Push the current branch\"\n+};\n+\n+static void warn_unconfigured_push()\n {\n-\tstruct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT;\n \tint i;\n+\tfor (i = 0; i < ARRAY_SIZE(warn_unconfigured_push_msg); i++)\n+\t\twarning(\"%s\", warn_unconfigured_push_msg[i]);\n+}\n+\n+\n+static int setup_default_remote_config(const char *name, const char *url, int push, struct string_list *track)\n+{\n+\tstruct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;\n+\tconst char *refspec = NULL;\n+\tint mirror = 0;\n+\tint current = 0;\n+\tint autosetuppush = 0;\n+\tint setup_push_refspecs = 0;\n+\tint i;\n+\n+\tswitch (push) {\n+\tcase PUSH_DEFAULT_UNSPECIFIED:\n+\t\twarn_unconfigured_push();\n+\t\t/* fallthrough */\n+\n+\tcase PUSH_DEFAULT_MATCHING:\n+\t\trefspec = \":\";\n+\t\tbreak;\n+\n+\tcase PUSH_DEFAULT_MIRROR:\n+\t\trefspec = \"+refs/*:refs/*\";\n+\t\tmirror = 1;\n+\t\tbreak;\n+\n+\tcase PUSH_DEFAULT_TRACKING:\n+\t\tcurrent = 1;\n+\t\tautosetuppush = 1;\n+\t\tsetup_push_refspecs = (track->nr > 0);\n+\t\tbreak;\n+\n+\tcase PUSH_DEFAULT_CURRENT:\n+\t\trefspec = \"HEAD\";\n+\t\tcurrent = 1;\n+\t\tbreak;\n+\n+\tcase PUSH_DEFAULT_NOTHING:\n+\t\tbreak;\n+\t}\n \n+\tif (refspec) {\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addf(&buf, \"remote.%s.push\", name);\n+\t\tif (git_config_set(buf.buf, refspec))\n+\t\t\treturn 1;\n+\t}\n+\tif (autosetuppush) {\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addf(&buf, \"remote.%s.autosetuppush\", name);\n+\t\tif (git_config_set(buf.buf, \"true\"))\n+\t\t\treturn 1;\n+\t}\n \tif (mirror) {\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_addf(&buf, \"remote.%s.mirror\", name);\n \t\tif (git_config_set(buf.buf, \"true\"))\n \t\t\treturn 1;\n \t}\n+\tif (current) {\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addf(&buf, \"remote.%s.pushHeadOnly\", name);\n+\t\tif (git_config_set(buf.buf, \"true\"))\n+\t\t\treturn 1;\n+\t}\n \n \tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"remote.%s.url\", name);\n@@ -55,6 +130,8 @@ static int setup_remote_config(const char *name, const char *url, int mirror, st\n \n \tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"remote.%s.fetch\", name);\n+\tstrbuf_reset(&buf3);\n+\tstrbuf_addf(&buf3, \"remote.%s.push\", name);\n \n \tif (track->nr == 0)\n \t\tstring_list_append(\"*\", track);\n@@ -62,8 +139,16 @@ static int setup_remote_config(const char *name, const char *url, int mirror, st\n \tfor (i = 0; i < track->nr; i++) {\n \t\tstruct string_list_item *item = track->items + i;\n \n+\t\tif (setup_push_refspecs) {\n+\t\t\tstrbuf_reset(&buf2);\n+\t\t\tstrbuf_addf(&buf2, \"refs/heads/%s:refs/heads/%s\",\n+\t\t\t\t    item->string, item->string);\n+\t\t\tif (git_config_set_multivar(buf3.buf, buf2.buf, \"^$\", 0))\n+\t\t\t\treturn 1;\n+\t\t}\n+\n \t\tstrbuf_reset(&buf2);\n-\t\tif (mirror)\n+\t\tif (push == PUSH_DEFAULT_MIRROR)\n \t\t\tstrbuf_addf(&buf2, \"+refs/%s:refs/%s\",\n \t\t\t\t\titem->string, item->string);\n \t\telse\n@@ -75,10 +160,24 @@ static int setup_remote_config(const char *name, const char *url, int mirror, st\n \n \tstrbuf_release(&buf);\n \tstrbuf_release(&buf2);\n+\tstrbuf_release(&buf3);\n \treturn 0;\n }\n \n \n+static int opt_parse_push(const struct option *opt, const char *arg, int not)\n+{\n+\tint *value = opt->value;\n+\tif (not)\n+\t\t*value = PUSH_DEFAULT_NOTHING;\n+\telse if (!arg)\n+\t\t*value = push_default;\n+\telse\n+\t\treturn git_parse_push_default(\"--push\", arg, value);\n+\n+\treturn 0;\n+}\n+\n static int opt_parse_track(const struct option *opt, const char *arg, int not)\n {\n \tstruct string_list *list = opt->value;\n@@ -104,7 +203,7 @@ static int fetch_remote(const char *name)\n \n static int add(int argc, const char **argv)\n {\n-\tint fetch = 0, mirror = 0;\n+\tint fetch = 0, push = PUSH_DEFAULT_UNKNOWN, mirror = 0;\n \tstruct string_list track = { NULL, 0, 0 };\n \tconst char *master = NULL;\n \tstruct remote *remote;\n@@ -117,10 +216,13 @@ static int add(int argc, const char **argv)\n \t\tOPT_CALLBACK('t', \"track\", &track, \"branch\",\n \t\t\t\"branch(es) to track\", opt_parse_track),\n \t\tOPT_STRING('m', \"master\", &master, \"branch\", \"master branch\"),\n+\t\t{ OPTION_CALLBACK, 0, \"push\", &push, \"strategy\",\n+\t\t\t\"how to setup pushing\", PARSE_OPT_OPTARG, opt_parse_push },\n \t\tOPT_BOOLEAN(0, \"mirror\", &mirror, \"no separate remotes\"),\n \t\tOPT_END()\n \t};\n \n+\tgit_config(git_default_config, NULL);\n \targc = parse_options(argc, argv, NULL, options, builtin_remote_usage,\n \t\t\t     0);\n \n@@ -130,6 +232,11 @@ static int add(int argc, const char **argv)\n \tname = argv[0];\n \turl = argv[1];\n \n+\tif (push == PUSH_DEFAULT_UNKNOWN)\n+\t\tpush = mirror ? PUSH_DEFAULT_MIRROR : PUSH_DEFAULT_NOTHING;\n+\telse if (mirror && push != PUSH_DEFAULT_MIRROR)\n+\t\t\tdie (\"--mirror and --push are incompatible\");\n+\n \tremote = remote_get(name);\n \tif (remote && (remote->url_nr > 1 || strcmp(name, remote->url[0]) ||\n \t\t\tremote->fetch_refspec_nr))\n@@ -139,7 +246,7 @@ static int add(int argc, const char **argv)\n \tif (!valid_fetch_refspec(buf2.buf))\n \t\tdie(\"'%s' is not a valid remote name\", name);\n \n-\tif (setup_remote_config(name, url, mirror, &track))\n+\tif (setup_remote_config(name, url, push, &track))\n \t\treturn 1;\n \n \tif (fetch && fetch_remote(name))\ndiff --git a/cache.h b/cache.h\nindex 172d36c..a46bfe6 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -544,7 +544,8 @@ enum rebase_setup_type {\n };\n \n enum push_default_type {\n-\tPUSH_DEFAULT_UNSPECIFIED = -1,\n+\tPUSH_DEFAULT_UNKNOWN = -2,\t/* command line: use push.default */\n+\tPUSH_DEFAULT_UNSPECIFIED = -1,\t/* config key absent */\n \tPUSH_DEFAULT_NOTHING = 0,\n \tPUSH_DEFAULT_MATCHING,\n \tPUSH_DEFAULT_TRACKING,\n@@ -640,6 +641,7 @@ enum sharedrepo {\n };\n int git_config_perm(const char *var, const char *value);\n int git_config_tracking(const char *var, const char *value, struct tracking_config *cfg);\n+int git_parse_push_default(const char *var, const char *value, int *result);\n int set_shared_perm(const char *path, int mode);\n #define adjust_shared_perm(path) set_shared_perm((path), 0)\n int safe_create_leading_directories(char *path);\ndiff --git a/config.c b/config.c\nindex 4db5c6d..37d95a4 100644\n--- a/config.c\n+++ b/config.c\n@@ -580,6 +580,27 @@ int git_tracking_config(const char *var, const char *value, struct tracking_conf\n \treturn 0;\n }\n \n+int git_parse_push_default(const char *var, const char *value, int *result)\n+{\n+\tif (!strcmp(value, \"nothing\"))\n+\t\t*result = PUSH_DEFAULT_NOTHING;\n+\telse if (!strcmp(value, \"matching\"))\n+\t\t*result = PUSH_DEFAULT_MATCHING;\n+\telse if (!strcmp(value, \"mirror\"))\n+\t\t*result = PUSH_DEFAULT_MIRROR;\n+\telse if (!strcmp(value, \"tracking\"))\n+\t\t*result = PUSH_DEFAULT_TRACKING;\n+\telse if (!strcmp(value, \"current\"))\n+\t\t*result = PUSH_DEFAULT_CURRENT;\n+\telse {\n+\t\terror(\"Malformed value for %s: %s\", var, value);\n+\t\treturn error(\"Must be one of nothing, matching, \"\n+\t\t\t     \"mirror, tracking or current.\");\n+\t}\n+\n+\treturn 0;\n+}\n+\n static int git_default_branch_config(const char *var, const char *value)\n {\n \tint result;\n@@ -600,22 +621,8 @@ static int git_default_push_config(const char *var, const char *value)\n \tif (!strcmp(var, \"push.default\")) {\n \t\tif (!value)\n \t\t\treturn config_error_nonbool(var);\n-\t\telse if (!strcmp(value, \"nothing\"))\n-\t\t\tpush_default = PUSH_DEFAULT_NOTHING;\n-\t\telse if (!strcmp(value, \"matching\"))\n-\t\t\tpush_default = PUSH_DEFAULT_MATCHING;\n-\t\telse if (!strcmp(value, \"mirror\"))\n-\t\t\tpush_default = PUSH_DEFAULT_MIRROR;\n-\t\telse if (!strcmp(value, \"tracking\"))\n-\t\t\tpush_default = PUSH_DEFAULT_TRACKING;\n-\t\telse if (!strcmp(value, \"current\"))\n-\t\t\tpush_default = PUSH_DEFAULT_CURRENT;\n-\t\telse {\n-\t\t\terror(\"Malformed value for %s: %s\", var, value);\n-\t\t\treturn error(\"Must be one of nothing, matching, \"\n-\t\t\t\t     \"mirror, tracking or current.\");\n-\t\t}\n-\t\treturn 0;\n+\t\telse\n+\t\t\treturn git_parse_push_default (var, value, &push_default);\n \t}\n \n \t/* Add other config variables here and to Documentation/config.txt. */\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex 852ccb5..a411eef 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -77,6 +77,79 @@ test_expect_success 'add another remote' '\n )\n '\n \n+test_expect_success 'configuration for --push=mirror' '\n+(\n+\tgit clone one test2 &&\n+\tcd test2 &&\n+\tgit remote add push-mirror --push=mirror ../two &&\n+\tgit config --bool remote.push-mirror.mirror &&\n+\t! git config --bool remote.push-mirror.pushHeadOnly &&\n+\t! git config --bool remote.push-mirror.autosetuppush &&\n+\ttest $(git config remote.push-mirror.push) = \"+refs/*:refs/*\"\n+)\n+'\n+\n+test_expect_success 'configuration for --push=current' '\n+(\n+\tcd test2 &&\n+\tgit remote add push-current --push=current ../two &&\n+\t! git config --bool remote.push-current.mirror &&\n+\tgit config --bool remote.push-current.pushHeadOnly &&\n+\t! git config --bool remote.push-current.autosetuppush &&\n+\ttest $(git config remote.push-current.push) = HEAD\n+)\n+'\n+\n+test_expect_success 'configuration for --push=matching' '\n+(\n+\tcd test2 &&\n+\tgit remote add -f push-matching --push=matching ../two &&\n+\t! git config --bool remote.push-matching.mirror &&\n+\t! git config --bool remote.push-matching.pushHeadOnly &&\n+\t! git config --bool remote.push-matching.autosetuppush &&\n+\ttest $(git config remote.push-matching.push) = :\n+)\n+'\n+\n+test_expect_success 'configuration for --push=tracking' '\n+(\n+\tgit clone one test3 &&\n+\tcd test3 &&\n+\tgit remote add -f push-tracking --push=tracking ../two &&\n+\t! git config --bool remote.push-tracking.tracking &&\n+\tgit config --bool remote.push-tracking.pushHeadOnly &&\n+\tgit config --bool remote.push-tracking.autosetuppush &&\n+\t! test $(git config remote.push-tracking.push) &&\n+\tgit checkout -b myother push-tracking/another &&\n+\ttest $(git config remote.push-tracking.push) = \"refs/heads/myother:refs/heads/another\"\n+)\n+'\n+\n+test_expect_success 'configuration for --push -t (push.default = tracking)' '\n+(\n+\tgit clone one test4 &&\n+\tcd test4 &&\n+\tgit config push.default tracking &&\n+\tgit remote add -f push-tracking2 -t another --push ../two &&\n+\t! git config --bool remote.push-tracking2.mirror &&\n+\tgit config --bool remote.push-tracking2.pushHeadOnly &&\n+\tgit config --bool remote.push-tracking2.autosetuppush &&\n+\ttest $(git config remote.push-tracking2.push) = \"refs/heads/another:refs/heads/another\"\n+)\n+'\n+\n+test_expect_success 'configuration for --push -t (push.default = matching)' '\n+(\n+\tcd test4 &&\n+\tgit config push.default matching\n+\tgit remote add -f push-matching2 -t side --push ../two &&\n+\t! git config --bool remote.push-matching2.mirror &&\n+\t! git config --bool remote.push-matching2.pushHeadOnly &&\n+\t! git config --bool remote.push-matching2.autosetuppush &&\n+\ttest $(git config remote.push-matching2.push) = :\n+)\n+'\n+\n test_expect_success 'remote forces tracking branches' '\n (\n \tcd test &&\ndiff --git a/t/t5517-push-mirror.sh b/t/t5517-push-mirror.sh\nindex ea49ded..7a0ff99 100755\n--- a/t/t5517-push-mirror.sh\n+++ b/t/t5517-push-mirror.sh\n@@ -225,7 +225,7 @@ test_expect_success 'push mirror adds, updates and removes tags together' '\n \n '\n \n-test_expect_success 'remote.foo.mirror adds and removes branches' '\n+test_expect_success 'git remote add --mirror adds and removes branches' '\n \n \tmk_repo_pair --mirror &&\n \t(\n@@ -245,6 +245,26 @@ test_expect_success 'remote.foo.mirror adds and removes branches' '\n \n '\n \n+test_expect_success 'git remote add --push=mirror adds and removes branches' '\n+\n+\tmk_repo_pair --push=mirror &&\n+\t(\n+\t\tcd master &&\n+\t\techo one >foo && git add foo && git commit -m one &&\n+\t\tgit branch keep master &&\n+\t\tgit branch remove master &&\n+\t\tgit push up &&\n+\t\tgit branch -D remove\n+\t\tgit push up\n+\t) &&\n+\t(\n+\t\tcd mirror &&\n+\t\tgit show-ref -s --verify refs/heads/keep &&\n+\t\tinvert git show-ref -s --verify refs/heads/remove\n+\t)\n+\n+'\n+\n test_expect_success 'remote.foo.mirror=no has no effect' '\n \n \tmk_repo_pair &&\n-- \n1.6.2.5\n"},{"id":"118337","messageId":"1248112195-3761-6-git-send-email-bonzini@gnu.org","threadId":"20170","inReplyTo":"1248112195-3761-1-git-send-email-bonzini@gnu.org","subject":"[PATCH RFC 5/8] clone: refactoring of building the fetch refspec","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-20T17:49:52Z","receivedAt":"2009-07-20T17:49:52Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"In builtin-clone.c, two variables (src_ref_prefix and branch_top.buf)\nwere used to represent basically two parts of a refspec.  Since a\nrefspec is built anyway later, we can drop these variables and\nuse refspec->dst when we need it.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n builtin-clone.c |   21 ++++++++++-----------\n 1 files changed, 10 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex 552ddf6..345101a 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -349,9 +349,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tint dest_exists;\n \tconst struct ref *refs, *head_points_at, *remote_head, *mapped_refs;\n \tstruct strbuf key = STRBUF_INIT, value = STRBUF_INIT;\n-\tstruct strbuf branch_top = STRBUF_INIT, reflog_msg = STRBUF_INIT;\n+\tstruct strbuf reflog_msg = STRBUF_INIT;\n \tstruct transport *transport = NULL;\n-\tchar *src_ref_prefix = \"refs/heads/\";\n \tint err = 0;\n \n \tstruct refspec *refspec;\n@@ -452,20 +451,21 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \tif (option_bare) {\n \t\tif (option_mirror)\n-\t\t\tsrc_ref_prefix = \"refs/\";\n-\t\tstrbuf_addstr(&branch_top, src_ref_prefix);\n+\t\t\tfetch_pattern = \"+refs/*:refs/*\";\n+\t\telse\n+\t\t\tfetch_pattern = \"+refs/heads/*:refs/heads/*\";\n \n \t\tgit_config_set(\"core.bare\", \"true\");\n \t} else {\n-\t\tstrbuf_addf(&branch_top, \"refs/remotes/%s/\", option_origin);\n+\t\tstrbuf_addf(&value, \"+refs/heads/*:refs/remotes/%s/*:*\",\n+\t\t\t    option_origin);\n+\t\tfetch_pattern = value.buf;\n \t}\n \n-\tstrbuf_addf(&value, \"+%s*:%s*\", src_ref_prefix, branch_top.buf);\n-\n \tif (option_mirror || !option_bare) {\n \t\t/* Configure the remote */\n \t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n-\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n+\t\tgit_config_set_multivar(key.buf, fetch_pattern, \"^$\", 0);\n \t\tstrbuf_reset(&key);\n \n \t\tif (option_mirror) {\n@@ -479,7 +479,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_reset(&key);\n \t}\n \n-\tfetch_pattern = value.buf;\n \trefspec = parse_fetch_refspec(1, &fetch_pattern);\n \n \tstrbuf_reset(&value);\n@@ -549,7 +548,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\t   head_points_at->old_sha1,\n \t\t\t\t   NULL, 0, DIE_ON_ERR);\n \n-\t\t\tstrbuf_addstr(&head_ref, branch_top.buf);\n+\t\t\tstrbuf_addstr(&head_ref, refspec->dst);\n+\t\t\tstrbuf_setlen(&head_ref, head_ref.len - 1);\n \t\t\tstrbuf_addstr(&head_ref, \"HEAD\");\n \n \t\t\t/* Remote branch link */\n@@ -611,7 +611,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \n \tstrbuf_release(&reflog_msg);\n-\tstrbuf_release(&branch_top);\n \tstrbuf_release(&key);\n \tstrbuf_release(&value);\n \tjunk_pid = 0;\n-- \n1.6.2.5\n"},{"id":"118341","messageId":"1248112195-3761-7-git-send-email-bonzini@gnu.org","threadId":"20170","inReplyTo":"1248112195-3761-1-git-send-email-bonzini@gnu.org","subject":"[PATCH RFC 6/8] clone: use setup_remote_config","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-20T17:49:53Z","receivedAt":"2009-07-20T17:49:53Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"We can now use setup_remote_config in builtin-clone.c and remove\nduplicated computation of remote.*.fetch contents.\n\nThe three cases to consider are:\n\n1) !option_bare.  This is the easy case when setup_remote_config just\nworks and a normal refs/heads/*:refs/remotes/origin/* refspec is created.\nThis only requires a small new feature in setup_remote_config to pass\nthe refspec back to builtin-clone.c; setup_remote_config will simply store\nit in the util field of the stringlist items.\n\n2) option_bare && option_mirror.  Again, setup_remote_config is used.\nPassing PUSH_DEFAULT_MIRROR to option_mirror will use +refs/*:refs/*\nfor the refspec.  There is a change from before, in that a remote.*.push\nrefspec will be setup as well.\n\n3) option_bare && !option_mirror.  In this case, no fetch refspec will\nbe configured (just as before), and setup_remote_config is not used.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n builtin-clone.c  |   46 ++++++++++++++--------------------------------\n builtin-remote.c |    5 +++--\n remote.h         |    3 +++\n 3 files changed, 20 insertions(+), 34 deletions(-)\n\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex 345101a..68d8a68 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -19,6 +19,7 @@\n #include \"strbuf.h\"\n #include \"dir.h\"\n #include \"pack-refs.h\"\n+#include \"string-list.h\"\n #include \"sigchain.h\"\n #include \"branch.h\"\n #include \"remote.h\"\n@@ -348,7 +349,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \tchar *path, *dir;\n \tint dest_exists;\n \tconst struct ref *refs, *head_points_at, *remote_head, *mapped_refs;\n-\tstruct strbuf key = STRBUF_INIT, value = STRBUF_INIT;\n \tstruct strbuf reflog_msg = STRBUF_INIT;\n \tstruct transport *transport = NULL;\n \tint err = 0;\n@@ -449,40 +449,24 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \tgit_config(git_default_config, NULL);\n \n-\tif (option_bare) {\n-\t\tif (option_mirror)\n-\t\t\tfetch_pattern = \"+refs/*:refs/*\";\n-\t\telse\n-\t\t\tfetch_pattern = \"+refs/heads/*:refs/heads/*\";\n-\n+\tif (option_bare)\n \t\tgit_config_set(\"core.bare\", \"true\");\n-\t} else {\n-\t\tstrbuf_addf(&value, \"+refs/heads/*:refs/remotes/%s/*:*\",\n-\t\t\t    option_origin);\n-\t\tfetch_pattern = value.buf;\n-\t}\n \n \tif (option_mirror || !option_bare) {\n-\t\t/* Configure the remote */\n-\t\tstrbuf_addf(&key, \"remote.%s.fetch\", option_origin);\n-\t\tgit_config_set_multivar(key.buf, fetch_pattern, \"^$\", 0);\n-\t\tstrbuf_reset(&key);\n-\n-\t\tif (option_mirror) {\n-\t\t\tstrbuf_addf(&key, \"remote.%s.mirror\", option_origin);\n-\t\t\tgit_config_set(key.buf, \"true\");\n-\t\t\tstrbuf_reset(&key);\n-\t\t}\n-\n-\t\tstrbuf_addf(&key, \"remote.%s.url\", option_origin);\n-\t\tgit_config_set(key.buf, repo);\n-\t\tstrbuf_reset(&key);\n+\t\tstruct string_list track = { NULL, 0, 0 };\n+\t\tsetup_remote_config (option_origin, repo,\n+\t\t\t\t     option_mirror\n+\t\t\t\t     ? PUSH_DEFAULT_MIRROR\n+\t\t\t\t     : PUSH_DEFAULT_NOTHING,\n+\t\t\t\t     &track);\n+\t\tfetch_pattern = track.items[0].util;\n+\t\trefspec = parse_fetch_refspec(1, &fetch_pattern);\n+\t\tstring_list_clear(&track, 1);\n+\t} else {\n+\t\tfetch_pattern = \"+refs/heads/*:refs/heads/*\";\n+\t\trefspec = parse_fetch_refspec(1, &fetch_pattern);\n \t}\n \n-\trefspec = parse_fetch_refspec(1, &fetch_pattern);\n-\n-\tstrbuf_reset(&value);\n-\n \tif (path && !is_bundle)\n \t\trefs = clone_local(path, git_dir);\n \telse {\n@@ -611,8 +595,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t}\n \n \tstrbuf_release(&reflog_msg);\n-\tstrbuf_release(&key);\n-\tstrbuf_release(&value);\n \tjunk_pid = 0;\n \treturn err;\n }\ndiff --git a/builtin-remote.c b/builtin-remote.c\nindex 23ab24b..99c06bc 100644\n--- a/builtin-remote.c\n+++ b/builtin-remote.c\n@@ -59,7 +59,7 @@ static void warn_unconfigured_push()\n }\n \n \n-static int setup_default_remote_config(const char *name, const char *url, int push, struct string_list *track)\n+int setup_remote_config(const char *name, const char *url, int push, struct string_list *track)\n {\n \tstruct strbuf buf = STRBUF_INIT, buf2 = STRBUF_INIT, buf3 = STRBUF_INIT;\n \tconst char *refspec = NULL;\n@@ -154,6 +154,7 @@ static int setup_default_remote_config(const char *name, const char *url, int pu\n \t\telse\n \t\t\tstrbuf_addf(&buf2, \"+refs/heads/%s:refs/remotes/%s/%s\",\n \t\t\t\t\titem->string, name, item->string);\n+\t\titem->util = xstrdup (buf2.buf);\n \t\tif (git_config_set_multivar(buf.buf, buf2.buf, \"^$\", 0))\n \t\t\treturn 1;\n \t}\n@@ -265,7 +266,7 @@ static int add(int argc, const char **argv)\n \n \tstrbuf_release(&buf);\n \tstrbuf_release(&buf2);\n-\tstring_list_clear(&track, 0);\n+\tstring_list_clear(&track, 1);\n \n \treturn 0;\n }\ndiff --git a/remote.h b/remote.h\nindex 86b18dc..64f4d58 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -115,6 +115,9 @@ struct ref *get_remote_ref(const struct ref *remote_refs, const char *name);\n  */\n int remote_find_tracking(struct remote *remote, struct refspec *refspec);\n \n+struct string_list;\n+int setup_remote_config(const char *name, const char *url, int push, struct string_list *track);\n+\n struct branch {\n \tconst char *name;\n \tconst char *refname;\n-- \n1.6.2.5\n"},{"id":"118338","messageId":"1248112195-3761-8-git-send-email-bonzini@gnu.org","threadId":"20170","inReplyTo":"1248112195-3761-1-git-send-email-bonzini@gnu.org","subject":"[PATCH RFC 7/8] config: add git_config_norepo","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-20T17:49:54Z","receivedAt":"2009-07-20T17:49:54Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This function will be needed to read the configuration in git-clone\nbefore the new repository is created.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n cache.h  |    1 +\n config.c |   37 ++++++++++++++++++++++++++++---------\n 2 files changed, 29 insertions(+), 9 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex a46bfe6..c349cd1 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -893,6 +893,7 @@ typedef int (*config_fn_t)(const char *, const char *, void *);\n extern int git_tracking_config(const char *, const char *, struct tracking_config *);\n extern int git_default_config(const char *, const char *, void *);\n extern int git_config_from_file(config_fn_t fn, const char *, void *);\n+extern int git_config_norepo(config_fn_t fn, void *);\n extern int git_config(config_fn_t fn, void *);\n extern int git_parse_ulong(const char *, unsigned long *);\n extern int git_config_int(const char *, const char *);\ndiff --git a/config.c b/config.c\nindex 37d95a4..d50a261 100644\n--- a/config.c\n+++ b/config.c\n@@ -709,19 +709,14 @@ int git_config_global(void)\n \treturn !git_env_bool(\"GIT_CONFIG_NOGLOBAL\", 0);\n }\n \n-int git_config(config_fn_t fn, void *data)\n+static int git_config_extra_repo(config_fn_t fn, void *data, int *found)\n {\n-\tint ret = 0, found = 0;\n-\tchar *repo_config = NULL;\n+\tint ret = 0;\n \tconst char *home = NULL;\n-\n-\t/* Setting $GIT_CONFIG makes git read _only_ the given config file. */\n-\tif (config_exclusive_filename)\n-\t\treturn git_config_from_file(fn, config_exclusive_filename, data);\n \tif (git_config_system() && !access(git_etc_gitconfig(), R_OK)) {\n \t\tret += git_config_from_file(fn, git_etc_gitconfig(),\n \t\t\t\t\t    data);\n-\t\tfound += 1;\n+\t\t*found += 1;\n \t}\n \n \thome = getenv(\"HOME\");\n@@ -729,11 +724,35 @@ int git_config(config_fn_t fn, void *data)\n \t\tchar *user_config = xstrdup(mkpath(\"%s/.gitconfig\", home));\n \t\tif (!access(user_config, R_OK)) {\n \t\t\tret += git_config_from_file(fn, user_config, data);\n-\t\t\tfound += 1;\n+\t\t\t*found += 1;\n \t\t}\n \t\tfree(user_config);\n \t}\n+\treturn ret;\n+}\n+\n+int git_config_norepo(config_fn_t fn, void *data)\n+{\n+\tint ret, found = 0;\n+\n+\t/* Setting $GIT_CONFIG makes git read _only_ the given config file. */\n+\tif (config_exclusive_filename)\n+\t\treturn git_config_from_file(fn, config_exclusive_filename, data);\n+\tret = git_config_extra_repo (fn, data, &found);\n+\tif (found == 0)\n+\t\treturn -1;\n+\treturn ret;\n+}\n \n+int git_config(config_fn_t fn, void *data)\n+{\n+\tint ret, found = 0;\n+\tchar *repo_config;\n+\n+\t/* Setting $GIT_CONFIG makes git read _only_ the given config file. */\n+\tif (config_exclusive_filename)\n+\t\treturn git_config_from_file(fn, config_exclusive_filename, data);\n+\tret = git_config_extra_repo (fn, data, &found);\n \trepo_config = git_pathdup(\"config\");\n \tif (!access(repo_config, R_OK)) {\n \t\tret += git_config_from_file(fn, repo_config, data);\n-- \n1.6.2.5\n"},{"id":"118339","messageId":"1248112195-3761-9-git-send-email-bonzini@gnu.org","threadId":"20170","inReplyTo":"1248112195-3761-1-git-send-email-bonzini@gnu.org","subject":"[PATCH RFC 8/8] clone: add --push option","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-20T17:49:55Z","receivedAt":"2009-07-20T17:49:55Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This patch completes the series by supporting --push within\ngit clone as well.\n\nThe bulk of the patch actually is just moving the options struct\nfor builtin-clone as late as possible to make it possible to use\nOPT_CALLBACK.  The actual implementation of --push is essentially\nreusing setup_remote_config, and actually simplifies the code.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n Documentation/git-clone.txt |   13 ++++++-\n builtin-clone.c             |   90 ++++++++++++++++++++++++++-----------------\n t/t5601-clone.sh            |   78 +++++++++++++++++++++++++++++++++++++\n 3 files changed, 145 insertions(+), 36 deletions(-)\n\ndiff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt\nindex b14de6c..d5753c4 100644\n--- a/Documentation/git-clone.txt\n+++ b/Documentation/git-clone.txt\n@@ -27,6 +27,9 @@ all the remote-tracking branches, and a `git pull` without\n arguments will in addition merge the remote master branch into the\n current master branch, if any.\n \n+Optionally, `git clone` can also set up the behavior upon `git push`.\n+This will be done only if the `--push` option is provided.\n+\n This default configuration is achieved by creating references to\n the remote branch heads under `$GIT_DIR/refs/remotes/origin` and\n by initializing `remote.origin.url` and `remote.origin.fetch`\n@@ -111,8 +114,16 @@ then the cloned repository will become corrupt.\n \tused, neither remote-tracking branches nor the related\n \tconfiguration variables are created.\n \n+--push=<strategy>::\n+\tSet up the clone's behavior upon `git push`.  Valid values\n+\tfor `\\--push` are `matching`, `mirror, `tracking`, `current`\n+\tand `nothing`.  The meanings are the same as for the\n+\t`push.default` configuration key.  If no strategy is\n+\tspecified, the contents of `push.default` will be used.\n+\n --mirror::\n-\tSet up a mirror of the remote repository.  This implies --bare.\n+\tSet up a mirror of the remote repository.  This is the\n+\tsame as `\\--push=mirror --bare`.\n \n --origin <name>::\n -o <name>::\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex 68d8a68..b9f2f16 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -40,40 +40,12 @@ static const char * const builtin_clone_usage[] = {\n \n static int option_quiet, option_no_checkout, option_bare, option_mirror;\n static int option_local, option_no_hardlinks, option_shared;\n+static int option_push = PUSH_DEFAULT_UNKNOWN;\n static char *option_template, *option_reference, *option_depth;\n static char *option_origin = NULL;\n static char *option_upload_pack = \"git-upload-pack\";\n static int option_verbose;\n \n-static struct option builtin_clone_options[] = {\n-\tOPT__QUIET(&option_quiet),\n-\tOPT__VERBOSE(&option_verbose),\n-\tOPT_BOOLEAN('n', \"no-checkout\", &option_no_checkout,\n-\t\t    \"don't create a checkout\"),\n-\tOPT_BOOLEAN(0, \"bare\", &option_bare, \"create a bare repository\"),\n-\tOPT_BOOLEAN(0, \"naked\", &option_bare, \"create a bare repository\"),\n-\tOPT_BOOLEAN(0, \"mirror\", &option_mirror,\n-\t\t    \"create a mirror repository (implies bare)\"),\n-\tOPT_BOOLEAN('l', \"local\", &option_local,\n-\t\t    \"to clone from a local repository\"),\n-\tOPT_BOOLEAN(0, \"no-hardlinks\", &option_no_hardlinks,\n-\t\t    \"don't use local hardlinks, always copy\"),\n-\tOPT_BOOLEAN('s', \"shared\", &option_shared,\n-\t\t    \"setup as shared repository\"),\n-\tOPT_STRING(0, \"template\", &option_template, \"path\",\n-\t\t   \"path the template repository\"),\n-\tOPT_STRING(0, \"reference\", &option_reference, \"repo\",\n-\t\t   \"reference repository\"),\n-\tOPT_STRING('o', \"origin\", &option_origin, \"branch\",\n-\t\t   \"use <branch> instead of 'origin' to track upstream\"),\n-\tOPT_STRING('u', \"upload-pack\", &option_upload_pack, \"path\",\n-\t\t   \"path to git-upload-pack on the remote\"),\n-\tOPT_STRING(0, \"depth\", &option_depth, \"depth\",\n-\t\t    \"create a shallow clone of that depth\"),\n-\n-\tOPT_END()\n-};\n-\n static char *get_repo_path(const char *repo, int *is_bundle)\n {\n \tstatic char *suffix[] = { \"/.git\", \".git\", \"\" };\n@@ -341,6 +313,50 @@ static struct ref *write_remote_refs(const struct ref *refs,\n \treturn local_refs;\n }\n \n+static int opt_parse_push(const struct option *opt, const char *arg, int not)\n+{\n+\tint *value = opt->value;\n+\tif (not)\n+\t\t*value = PUSH_DEFAULT_NOTHING;\n+\telse if (!arg)\n+\t\t*value = push_default;\n+\telse\n+\t\treturn git_parse_push_default(\"--push\", arg, value);\n+\n+\treturn 0;\n+}\n+\n+static struct option builtin_clone_options[] = {\n+\tOPT__QUIET(&option_quiet),\n+\tOPT__VERBOSE(&option_verbose),\n+\tOPT_BOOLEAN('n', \"no-checkout\", &option_no_checkout,\n+\t\t    \"don't create a checkout\"),\n+\tOPT_BOOLEAN(0, \"bare\", &option_bare, \"create a bare repository\"),\n+\tOPT_BOOLEAN(0, \"naked\", &option_bare, \"create a bare repository\"),\n+\t{ OPTION_CALLBACK, 0, \"push\", &option_push, \"strategy\",\n+\t\t\"how to setup pushing\", PARSE_OPT_OPTARG, opt_parse_push },\n+\tOPT_BOOLEAN(0, \"mirror\", &option_mirror,\n+\t\t    \"create a mirror repository (implies --bare and --push=mirror)\"),\n+\tOPT_BOOLEAN('l', \"local\", &option_local,\n+\t\t    \"to clone from a local repository\"),\n+\tOPT_BOOLEAN(0, \"no-hardlinks\", &option_no_hardlinks,\n+\t\t    \"don't use local hardlinks, always copy\"),\n+\tOPT_BOOLEAN('s', \"shared\", &option_shared,\n+\t\t    \"setup as shared repository\"),\n+\tOPT_STRING(0, \"template\", &option_template, \"path\",\n+\t\t   \"path the template repository\"),\n+\tOPT_STRING(0, \"reference\", &option_reference, \"repo\",\n+\t\t   \"reference repository\"),\n+\tOPT_STRING('o', \"origin\", &option_origin, \"branch\",\n+\t\t   \"use <branch> instead of 'origin' to track upstream\"),\n+\tOPT_STRING('u', \"upload-pack\", &option_upload_pack, \"path\",\n+\t\t   \"path to git-upload-pack on the remote\"),\n+\tOPT_STRING(0, \"depth\", &option_depth, \"depth\",\n+\t\t    \"create a shallow clone of that depth\"),\n+\n+\tOPT_END()\n+};\n+\n int cmd_clone(int argc, const char **argv, const char *prefix)\n {\n \tint is_bundle = 0;\n@@ -358,14 +374,22 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \tjunk_pid = getpid();\n \n+\tgit_config_norepo(git_default_config, NULL);\n \targc = parse_options(argc, argv, prefix, builtin_clone_options,\n \t\t\t     builtin_clone_usage, 0);\n \n \tif (argc == 0)\n \t\tdie(\"You must specify a repository to clone.\");\n \n-\tif (option_mirror)\n+\tif (option_push == PUSH_DEFAULT_UNKNOWN)\n+\t\toption_push = (option_mirror\n+\t\t\t       ? PUSH_DEFAULT_MIRROR\n+\t\t\t       : PUSH_DEFAULT_NOTHING);\n+\tif (option_mirror) {\n \t\toption_bare = 1;\n+\t\tif (option_push != PUSH_DEFAULT_MIRROR)\n+\t\t\tdie (\"--mirror and --push are incompatible\");\n+\t}\n \n \tif (option_bare) {\n \t\tif (option_origin)\n@@ -454,11 +478,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \n \tif (option_mirror || !option_bare) {\n \t\tstruct string_list track = { NULL, 0, 0 };\n-\t\tsetup_remote_config (option_origin, repo,\n-\t\t\t\t     option_mirror\n-\t\t\t\t     ? PUSH_DEFAULT_MIRROR\n-\t\t\t\t     : PUSH_DEFAULT_NOTHING,\n-\t\t\t\t     &track);\n+\t\tsetup_remote_config(option_origin, repo, option_push, &track);\n \t\tfetch_pattern = track.items[0].util;\n \t\trefspec = parse_fetch_refspec(1, &fetch_pattern);\n \t\tstring_list_clear(&track, 1);\ndiff --git a/t/t5601-clone.sh b/t/t5601-clone.sh\nindex 2335d8b..6445dad 100755\n--- a/t/t5601-clone.sh\n+++ b/t/t5601-clone.sh\n@@ -83,11 +83,89 @@ test_expect_success 'clone --mirror' '\n \ttest ! -f mirror/file &&\n \tFETCH=\"$(cd mirror && git config remote.origin.fetch)\" &&\n \ttest \"+refs/*:refs/*\" = \"$FETCH\" &&\n+\tPUSH=\"$(cd mirror && git config remote.origin.push)\" &&\n+\ttest \"+refs/*:refs/*\" = \"$PUSH\" &&\n \tMIRROR=\"$(cd mirror && git config --bool remote.origin.mirror)\" &&\n \ttest \"$MIRROR\" = true\n \n '\n \n+test_expect_success 'clone --push=mirror' '\n+\n+\tgit clone --push=mirror src mirrorx &&\n+\ttest -f mirrorx/.git/HEAD &&\n+\ttest -f mirrorx/file &&\n+\tFETCH=\"$(cd mirrorx && git config remote.origin.fetch)\" &&\n+\ttest \"+refs/*:refs/*\" = \"$FETCH\" &&\n+\tPUSH=\"$(cd mirrorx && git config remote.origin.push)\" &&\n+\ttest \"+refs/*:refs/*\" = \"$PUSH\" &&\n+\tMIRROR=\"$(cd mirrorx && git config --bool remote.origin.mirror)\" &&\n+\ttest \"$MIRROR\" = true\n+\n+'\n+\n+test_expect_success 'clone --push=current' '\n+\n+\tgit clone --push=current src current &&\n+\ttest -f current/.git/HEAD &&\n+\ttest -f current/file &&\n+\t! (cd current/.git && git config --bool remote.origin.mirror) &&\n+\t(cd current/.git && git config --bool remote.origin.pushHeadOnly) &&\n+\t! (cd current/.git && git config --bool remote.origin.autosetuppush) &&\n+\ttest \"+refs/heads/*:refs/remotes/origin/*\" = \\\n+\t\t\"$(cd current && git config remote.origin.fetch)\" &&\n+\ttest HEAD = \"$(cd current && git config remote.origin.push)\"\n+\n+'\n+\n+test_expect_success 'clone --push' '\n+\n+\tHOME=$(pwd) &&\n+\texport HOME &&\n+\ttest_config=\"$HOME/.gitconfig\" &&\n+\tunset GIT_CONFIG_NOGLOBAL &&\n+\techo \"[push] default=current\" > $test_config\n+\tgit clone --push src default &&\n+\ttest -f default/.git/HEAD &&\n+\ttest -f default/file &&\n+\t! (cd default/.git && git config --bool remote.origin.mirror) &&\n+\t(cd default/.git && git config --bool remote.origin.pushHeadOnly) &&\n+\t! (cd default/.git && git config --bool remote.origin.autosetuppush) &&\n+\ttest \"+refs/heads/*:refs/remotes/origin/*\" = \\\n+\t\t\"$(cd default && git config remote.origin.fetch)\" &&\n+\ttest HEAD = \"$(cd default && git config remote.origin.push)\"\n+\n+'\n+\n+test_expect_success 'clone --push=tracking' '\n+\n+\tgit clone --push=tracking src tracking &&\n+\ttest -f tracking/.git/HEAD &&\n+\ttest -f tracking/file &&\n+\t! (cd tracking && git config --bool remote.origin.mirror) &&\n+\t(cd tracking && git config --bool remote.origin.pushHeadOnly) &&\n+\t(cd tracking && git config --bool remote.origin.autosetuppush) &&\n+\ttest \"+refs/heads/*:refs/remotes/origin/*\" = \\\n+\t\t\"$(cd tracking && git config remote.origin.fetch)\" &&\n+\ttest refs/heads/master:refs/heads/master = \\\n+\t\t\"$(cd tracking && git config remote.origin.push)\"\n+\n+'\n+\n+test_expect_success 'clone --push=matching' '\n+\n+\tgit clone --push=matching src matching &&\n+\ttest -f matching/.git/HEAD &&\n+\ttest -f matching/file &&\n+\t! (cd matching && git config --bool remote.origin.mirror) &&\n+\t! (cd matching && git config --bool remote.origin.pushHeadOnly) &&\n+\t! (cd matching && git config --bool remote.origin.autosetuppush) &&\n+\ttest \"+refs/heads/*:refs/remotes/origin/*\" = \\\n+\t\t\"$(cd matching && git config remote.origin.fetch)\" &&\n+\ttest : = \"$(cd matching && git config remote.origin.push)\"\n+\n+'\n+\n test_expect_success 'clone --bare names the local repository <name>.git' '\n \n \tgit clone --bare src &&\n-- \n1.6.2.5\n"},{"id":"118343","messageId":"7vocrfulpu.fsf@alter.siamese.dyndns.org","threadId":"20170","inReplyTo":"1248112195-3761-3-git-send-email-bonzini@gnu.org","subject":"Re: [PATCH RFC 2/8] push: add push.default = mirror","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-07-20T20:46:05Z","receivedAt":"2009-07-20T20:46:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paolo Bonzini <bonzini@gnu.org> writes:\n\n> This patch adds a new value for push.default.  The aim of the series is\n> to support all push.default values as arguments to `--push' in git-clone\n> and git-remote, and if push.default=mirror works it is easy to make\n> `--mirror' a synonym for `--push=mirror' in those comments.\n>\n> Signed-off-by: Paolo Bonzini <bonzini@gnu.org>\n> ---\n>  Documentation/config.txt |    2 ++\n>  builtin-push.c           |   12 ++++++++++--\n>  cache.h                  |    1 +\n>  config.c                 |    4 +++-\n>  4 files changed, 16 insertions(+), 3 deletions(-)\n>\n> diff --git a/Documentation/config.txt b/Documentation/config.txt\n> index 4c27e9d..fa5eb76 100644\n> --- a/Documentation/config.txt\n> +++ b/Documentation/config.txt\n> @@ -1290,6 +1290,8 @@ push.default::\n>  * `matching` push all matching branches.\n>    All branches having the same name in both ends are considered to be\n>    matching. This is the default.\n> +* `mirror` pushes all branches forcing non fast-forward updates and\n> +  deletes branches that do not exist anymore locally.\n>  * `tracking` push the current branch to its upstream branch.\n>  * `current` push the current branch to a branch of the same name.\n\nI think this patch alone (regardless of the rest which I haven't read)\nprobably makes sense.\n\nExcept that I think the part below contradicts with the --mirror push\nsemantics (see remote.c::match_refs()).\n\n> diff --git a/builtin-push.c b/builtin-push.c\n> index e678a9d..8a312a3 100644\n> --- a/builtin-push.c\n> +++ b/builtin-push.c\n> @@ -74,6 +73,10 @@ static void setup_default_push_refspecs(void)\n>  \t\tadd_refspec(\":\");\n>  \t\tbreak;\n>  \n> +\tcase PUSH_DEFAULT_MIRROR:\n> +\t\tadd_refspec(\"+refs/*:refs/*\");\n> +\t\tbreak;\n> +\n"},{"id":"118346","messageId":"4A64DE38.5090805@gmail.com","threadId":"20170","inReplyTo":"7vocrfulpu.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH RFC 2/8] push: add push.default = mirror","fromName":"Paolo Bonzini","fromEmail":"paolo.bonzini@gmail.com","sentAt":"2009-07-20T21:14:32Z","receivedAt":"2009-07-20T21:14:32Z","isPatch":true,"sender":{"key":"paolo.bonzini@gmail.com","avatar":"https://gravatar.com/avatar/7817ef2e168b4ef0570c5bb5bdc1d4b44f34d3075fe32b871710dd942d0a89f5?d=mp&s=160"},"body":"\n> I think this patch alone (regardless of the rest which I haven't read)\n> probably makes sense.\n>\n> Except that I think the part below contradicts with the --mirror push\n> semantics (see remote.c::match_refs()).\n\n        if (!send_mirror && prefixcmp(src->name, \"refs/heads/\"))\n                 continue;\n\nThe way I read this, send_mirror will send everything, even if it's\noutside refs/heads/*.  So refs/*.\n\nIf we agree on the semantics, would you harvest it separately or should \nI resubmit?\n\nPaolo\n"},{"id":"118348","messageId":"7vhbx7ujhq.fsf@alter.siamese.dyndns.org","threadId":"20170","inReplyTo":"4A64DE38.5090805@gmail.com","subject":"Re: [PATCH RFC 2/8] push: add push.default = mirror","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-07-20T21:34:09Z","receivedAt":"2009-07-20T21:34:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paolo Bonzini <paolo.bonzini@gmail.com> writes:\n\n>> I think this patch alone (regardless of the rest which I haven't read)\n>> probably makes sense.\n>>\n>> Except that I think the part below contradicts with the --mirror push\n>> semantics (see remote.c::match_refs()).\n>\n>        if (!send_mirror && prefixcmp(src->name, \"refs/heads/\"))\n>                 continue;\n>\n> The way I read this, send_mirror will send everything, even if it's\n> outside refs/heads/*.  So refs/*.\n\nAh, ok, thanks.\n\n> If we agree on the semantics, would you harvest it separately or\n> should I resubmit?\n\nI do not expect the rest of the series to be near 'next' before 1.6.4, and\nit is likely that I'll forget about this patch by the time 1.6.4 finally\nships.\n\nBut I realize that without the rest of the series, being able to say\npush.default = mirror does not add much value.  A replacement commit log\nmessage I wrote for this patch stand-alone is:\n\n    Instead \"git push --mirror $over_there\", you can say \"git push\" after\n    setting up push.default to \"mirror\".\n\nBut the thing is, you can already do that with \"remote add --mirror\".\n"},{"id":"118349","messageId":"4A64E342.6010307@gmail.com","threadId":"20170","inReplyTo":"7vhbx7ujhq.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH RFC 2/8] push: add push.default = mirror","fromName":"Paolo Bonzini","fromEmail":"paolo.bonzini@gmail.com","sentAt":"2009-07-20T21:36:02Z","receivedAt":"2009-07-20T21:36:02Z","isPatch":true,"sender":{"key":"paolo.bonzini@gmail.com","avatar":"https://gravatar.com/avatar/7817ef2e168b4ef0570c5bb5bdc1d4b44f34d3075fe32b871710dd942d0a89f5?d=mp&s=160"},"body":"\n>> If we agree on the semantics, would you harvest it separately or\n>> should I resubmit?\n>\n> I do not expect the rest of the series to be near 'next' before 1.6.4, and\n> it is likely that I'll forget about this patch by the time 1.6.4 finally\n> ships.\n\nOkay, let's keep it out of the way for now.  It doesn't add much.\n\nPaolo\n"},{"id":"118352","messageId":"7vprbvt30u.fsf@alter.siamese.dyndns.org","threadId":"20170","inReplyTo":"1248112195-3761-1-git-send-email-bonzini@gnu.org","subject":"Re: [PATCH RFC 0/8] introduce 'git remote add --push' and 'git clone --push'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-07-20T22:15:13Z","receivedAt":"2009-07-20T22:15:13Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paolo Bonzini <bonzini@gnu.org> writes:\n\n> I post as an initial RFC the third series in the push.default saga.\n> This is on top of origin/next and even then it requires the\n> \"push --current\" patches to be fully functional.  Even without it,\n> however, it will create correct configuration.\n>\n> The series adds --push options to \"git remote add\" and \"git clone\".\n> These accept a push strategy of the same kind as \"push.default\",\n> and will use it to create push configuration and refspecs.  These\n> will then override push.default.\n\nLet's step back a bit.\n\nIf I have a local branch X, is it conceivable that if I ever want to push\nit out to elsewhere on a regular basis, I would likely to push it to the\nsame branch at the same remote?\n\nI think the answer to the above question is yes, and partly because that\nis because the precondition of the question is qualified with \"on a\nregular basis\".\n\nNow, I regularly push my 'master' to four repositories (k.org, repo.or.cz,\nsf.net and sourceforge.jp), and I could be pushing it out to a different\nbranch at each of these remotes, but we could loosen the above statement\nsomewhat and still keep the main point.\n\n    If I have a local branch X that want to push it out to another\n    repository R on a regular basis, I would very likely to push it to the\n    same branch Y at that remote.\n\nNow, how do (X,R) and Y related with each other?  I think there are three\nworkflows that want different settings (and one is actually a special case\nof another one, so essentially there are only two).\n\n * X tracks Y from R, iow, branch.X.remote = R, branch.X.merge = Y;\n   push.default = tracking helps pushing out X when X is the current\n   branch, but it does not help pushing all such X out.\n\n * X is Y; push.default = matching helps pushing all such X out.  This is\n   a special case of the above.\n\n * X tracks Y from R, but there are other X' that also track Y from R and\n   pushing all of them is nonsense.  push.default = tracking helps pushing\n   X when it is the current branch.  This is the case where you fork your\n   topic(s) directly from remote integration branch\n\nAre these all?  What I am trying to get at is if we can tweak the rules\nwithout introducing too much configuration variables to cover all these\ncases.\n\nTraditionally, we said:\n\n    $ git push $there $ref\n\nis _always_ a shorthand for\n\n    $ git push $there $ref:$ref\n\nThis favors the second case in that if for whatever reason you cannot\nafford to push all of them in matching, but as long as your (X,R) -> Y\nmapping is X==Y (i.e. matching), then you do not have to say colon to\nduplicate refname.  You can say \"git push origin master next\" to push only\nthese two without pushing out 'pu', for example.  If we somehow tweak\nthis \"$ref is a shorthand for $ref:$ref\" rule to account for the tracking\nbranch.*.merge gives us, perhaps we can make the push easier to use.\n\nIf the conjecture \"no matter what your workflow is, (X,R) -> Y is a\nfunction, not a one-to-many-mapping\" holds, perhaps we may instead want to\nsay something like:\n\n (1) \"git push R\" pushes out the refs according to remote.R.push refspec\n     rules (this is also the same as the current set of rules).  Absense\n     of such configured refspec rules used to always trigger \"matching\"\n     rule, but now it can optionally use \"tracking\" rule for all the local\n     branches.\n\n (2) \"git push R $ref\" is *NOT* same as \"git push R $ref:$ref\" anymore.\n     Because for a given (R,X) we can say what (R,X) -> Y function yields,\n     we should map the given ref to where the user wants to put it.  If X\n     tracks Y, \"git push R X\" should become \"git push R X:Y\" without any\n     funky configuration.\n\n (3) \"git push $ref\" used to be illegal, but when it is unambiguous that\n     $ref cannot name a remote, we look at branch.$ref.remote = R to find\n     that the push is a shorthand for \"git push R $ref\".  The mapping rules\n     of (2) also applies.\n\n (4) \"git push\" is a synonym for \"git push R\" where R is the value of\n     branch.X.remote, or \"origin\" if there is no such configuration.  This\n     will in turn trigger rule (1) above.\n\n     We could optionally make it a synonym for \"git push X\" (where X is\n     the name of the current branch), which would invoke rule (3) above,\n     which in turn would invoke rule (2) above.  Perhaps \"push only the\n     current branch\" option in the configuration, or \"git push HEAD\" from\n     the command line, would trigger this alternate behaviour.\n\nI think one of the workflows quoted as the original motivation of Finn\nArne's series that added push.default also falls naturally out of this.\nWhen you interact with more than one remote, you may track the 'master'\nbranch from remote R1 as your local R1-master while tracking the 'master'\nbranch from remote R2 as your local R2-master.\n\nThen\n\n\t$ git push\n\nwhile on R1-master, with the optional setting of rule (4) above, will be\nthe same as\n\n\t$ git push R1-master\n\nboth of which would mean\n\n\t$ git push R1 R1-master:master\n\nAlso\n\n\t$ git push R1\n\nwill trigger rule (1) and would push whatever is configured in\nremote.R1.push.  Optionally, without remote.R1.push, it can inspect local\nbranches and find ones that track branches from R1, and push them to\ncorresponding places (this would match \"matching\" ref behaviour but with\nthe renaming expressed by branch.*.merge).\n\nJut thinking aloud.\n"},{"id":"118366","messageId":"4A659992.4020906@gnu.org","threadId":"20170","inReplyTo":"7vprbvt30u.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH RFC 0/8] introduce 'git remote add --push' and 'git clone --push'","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-21T10:33:54Z","receivedAt":"2009-07-21T10:33:54Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"\n> If I have a local branch X, is it conceivable that if I ever want to push\n> it out to elsewhere on a regular basis, I would likely to push it to the\n> same branch at the same remote?\n\nYes.  The exception could be if you push to multiple remotes---but in \nthat case, you have to issue multiple push commands anyway.  X -> (R,Y) \nis not a function, but (X,R) -> Y is.\n\n> Are these all?  What I am trying to get at is if we can tweak the rules\n> without introducing too much configuration variables to cover all these\n> cases.\n>\n> Traditionally, we said:\n>\n>      $ git push $there $ref\n>\n> is _always_ a shorthand for\n>\n>      $ git push $there $ref:$ref\n >\n> If we somehow tweak\n> this \"$ref is a shorthand for $ref:$ref\" rule to account for the tracking\n> branch.*.merge gives us, perhaps we can make the push easier to use.\n\nThis is nice indeed.  My questions are:\n\n1) can we assume backwards compatibility is not a problem?  Or, in other \nwords, would this be 1.6.5 or 1.7.0 material?  In the past, the list has \nusually been very cautious about that, but if there's good reasons I \nguess the answer can be \"yes\".\n\n>   (4) \"git push\" is a synonym for \"git push R\" where R is the value of\n>       branch.X.remote, or \"origin\" if there is no such configuration.  This\n>       will in turn trigger rule (1) above.\n>\n>       We could optionally make it a synonym for \"git push X\" (where X is\n>       the name of the current branch), which would invoke rule (3) above,\n>       which in turn would invoke rule (2) above.  Perhaps \"push only the\n>       current branch\" option in the configuration, or \"git push HEAD\" from\n>       the command line, would trigger this alternate behaviour.\n\n[i.e., looking up branch.*.remote for the current branch].\n\n2) if I understand correctly, then \"git push HEAD\" would behave like \npush.default=tracking rather than push.default=current.  If so, how do \nyou provide push.default=current behavior?  (Or maybe you don't care).\n\nWould this apply to remote.*.push = HEAD too?  (I think so).  Are the \nbackward compatibility issues stronger in this case?  (I also think so).\n\n3) if the previous item turns out to be a problem, should we invent a \nnew kind of refspec?  \"HEAD:\" is free to use, is it a good idea?  (I \ndon't like it).\n\n4) From an implementation point of view, how well will it fit the \nexisting code, or rather will it have to be special-cased all over the \nplace?  I already didn't like the single special case I have in the \"git \npush --current\" patch, though I think it's unavoidable.\n\n> I think one of the workflows quoted as the original motivation of Finn\n> Arne's series that added push.default also falls naturally out of this.\n\nThat's true, and I actually liked the idea very much.  The fact that it \nraises many questions is not necessarily a point down---adding new \nconfiguration keys is definitely simpler to flesh out, but it may be \nthat it only reaches a local maximum of simplicity/expressiveness.  In \nmy case the combination refspec/autosetuppush/pushHeadOnly is very \nexpressive, but it requires to be wrapped by porcelain \"git remote \nadd\"/\"git clone\" for the average user.\n\nIn the past I've seen the development of git follow the line of adding \nmore knobs to allow the experts to tweak them, but that could also be a \nbyproduct of the audience that this ML reaches.\n\nIn this case, I think the choice depends on how much effort can be \ninvested in answering the above questions.\n\nPaolo\n"},{"id":"118428","messageId":"7vtz157nvf.fsf@alter.siamese.dyndns.org","threadId":"20170","inReplyTo":"4A659992.4020906@gnu.org","subject":"Re: [PATCH RFC 0/8] introduce 'git remote add --push' and 'git clone --push'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-07-21T21:00:20Z","receivedAt":"2009-07-21T21:00:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Paolo Bonzini <bonzini@gnu.org> writes:\n\n>> If I have a local branch X, is it conceivable that if I ever want to push\n>> it out to elsewhere on a regular basis, I would likely to push it to the\n>> same branch at the same remote?\n>\n> Yes.  The exception could be if you push to multiple remotes---but in\n> that case, you have to issue multiple push commands anyway.  X ->\n> (R,Y) is not a function, but (X,R) -> Y is.\n>\n>> Are these all?  What I am trying to get at is if we can tweak the rules\n>> without introducing too much configuration variables to cover all these\n>> cases.\n>>\n>> Traditionally, we said:\n>>\n>>      $ git push $there $ref\n>>\n>> is _always_ a shorthand for\n>>\n>>      $ git push $there $ref:$ref\n>>\n>> If we somehow tweak\n>> this \"$ref is a shorthand for $ref:$ref\" rule to account for the tracking\n>> branch.*.merge gives us, perhaps we can make the push easier to use.\n>\n> This is nice indeed.  My questions are:\n>\n> 1) can we assume backwards compatibility is not a problem?  Or, in\n> other words, would this be 1.6.5 or 1.7.0 material?  In the past, the\n> list has usually been very cautious about that, but if there's good\n> reasons I guess the answer can be \"yes\".\n\nI do not think this is an 1.6.X material.  We _do_ plan a backward\nincompatible changes in 1.7.0 (a few series queued in 'pu') in the area,\nso if a redesign along these lines is preferable, 1.7.0 would be the\nmilestone to do so.\n\nI have a suspicion that the compatibility might not even be a huge issue\nfor this particular change.\n\n * If I say \"git push k.org master\", I always mean \"git push k.org\n   master:master\", but I would never have forked my master from something\n   else over there to begin with, and looking up branch.master.merge will\n   yield master and there is no change in behaviour.  Note that in this\n   paragraph, \"I\" is figurative---for anybody who uses local branch X as a\n   local integration branch to be pushed back to the remote X, the end\n   result will not change, even though the mechanism may be different.\n\n * For somebody who uses local branch Y to track branch X from the remote,\n   branch.X.merge would say Y.  The above change will not be backward\n   compatible in that:\n\n\t$ git push $there X\n\n   would start pushing X to Y (currently X is pushed to X).\n\n   But I suspect that people in this camp would already consider the\n   current behaviour a misfeature, and they are always doing:\n\n\t$ git push $there X:Y\n\n   when they want to push a single branch out, and loathing it (we can be\n   reasonably sure about this when we realize that push.default = tracking\n   was an attempt to lose :Y from this command line).\n\n   The change would be an improvement for them.  Of course when they\n   really mean it, they can say:\n\n\t$ git push $there X:X\n\n> 2) if I understand correctly, then \"git push HEAD\" would behave like\n> push.default=tracking rather than push.default=current.  If so, how do\n> you provide push.default=current behavior?  (Or maybe you don't care).\n>\n> Would this apply to remote.*.push = HEAD too?  (I think so).  Are the\n> backward compatibility issues stronger in this case?  (I also think\n> so).\n\nIn either case, one assumption I did not spell out in the message you are\nreplying to is that in a sane workflow the mapping branch.X.merge = Y\ngives us is enough and the (implicit) mapping remote.R.push gives to X is\nredundant information.  I.e. if branch.X.merge = Y, branch.X.remote = R,\nthen remote.R.push would never map local X to anything but remote Y.\n\nBut that is just an assumption.  While I do not think of a sane workflow\nfor which that assumption does not hold, it is not a proof that the\nassumption is universally true.\n\n\"git push HEAD\" would behave like either tracking or current after this\nchange, but the distinction does not matter in practice exactly for the\nsame reason as the answer to the item 1) above in your list.\n\n> 3) if the previous item turns out to be a problem, should we invent a\n> new kind of refspec?  \"HEAD:\" is free to use, is it a good idea?  (I\n> don't like it).\n\nLikewise, if it is inconvenient that \"git push HEAD\" follows\nbranch.X.merge while on branch X, you can say \"git push HEAD:HEAD\" to be\nmore explicit.\n\n> 4) From an implementation point of view, how well will it fit the\n> existing code, or rather will it have to be special-cased all over the\n> place?  I already didn't like the single special case I have in the\n> \"git push --current\" patch, though I think it's unavoidable.\n\nI actually have no idea, as I haven't looked at the code nor tried to\nremember how it currently does its thing, while writing the message you\nare replying to.\n\nThat was why I said \"let's step back a bit\".  I wanted to see if it is a\ngood time to start thinking what a design that is easier to use purely\nfrom the end-user standpoint should look like, without being constrained\nby the current implementation.  Piling more knobs after knobs may allow\nexperts who know which knobs to turn and what combination of knobs to\navoid in order to get the desired effect, but it is suboptimal if the end\nresult is unusable to ordinary users.\n\n> That's true, and I actually liked the idea very much.  The fact that\n> it raises many questions is not necessarily a point down---adding new\n> configuration keys is definitely simpler to flesh out, but it may be\n> that it only reaches a local maximum of simplicity/expressiveness.\n\nYes, the simplification (of the end user experience, anyway) I outlined\ncrucially depends on that one assumption that remote.R.push is more or\nless redundant information when branch.X.merge is properly used.\n\nI do not know if that is true.\n"}]}