{"thread":{"id":"26918","subject":"checkout new branch tracks wrong remote (bug?)","startedAt":"2011-03-30T02:27:31Z","lastAt":"2011-03-31T12:59:51Z","messageCount":13,"participants":["chris","Jeff King","Junio C Hamano"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"164652","messageId":"loom.20110330T040437-823@post.gmane.org","threadId":"26918","inReplyTo":null,"subject":"checkout new branch tracks wrong remote (bug?)","fromName":"chris","fromEmail":"jugg@hotmail.com","sentAt":"2011-03-30T02:27:31Z","receivedAt":"2011-03-30T02:27:31Z","isPatch":false,"sender":{"key":"jugg@hotmail.com","avatar":null},"body":"I have two remotes configured.\n\nOne is \"origin\" which has a local tracking branch \"master\" for \"origin/master\".\n\nThe other is \"mirror\" which has option mirror = true\n\nWhile on the local branch master, I issue the command:\n\n$ git checkout -b wip\n\nThe branch \"wip\" is created and oddly configured to track the \"mirror\" remote.\n\nHere is the .git/config after the \"wip\" branch was created:\n\n[core]\n        repositoryformatversion = 0\n        filemode = true\n        bare = false\n        logallrefupdates = true\n[remote \"origin\"]\n        fetch = +refs/heads/*:refs/remotes/origin/*\n        url = ssh://myserver.com/srv/git/myproject.git\n[branch \"master\"]\n        remote = origin\n        merge = refs/heads/master\n[remote \"mirror\"]\n        url = ssh://chris@myserver.com/srv/git/mirrors/chris/myproject.git\n        fetch = +refs/*:refs/*\n        mirror = true\n[branch \"wip\"]\n        remote = mirror\n        merge = refs/heads/master\n\n$ git --version\ngit version 1.7.4.1\n$ git config branch.autosetupmerge\n$\n\nI do not expect this \"wip\" branch to be tracking the \"mirror\" remote, but rather\n\"origin\", according to the documentation.\n\nchris\n"},{"id":"164691","messageId":"20110330145908.GA812@sigill.intra.peff.net","threadId":"26918","inReplyTo":"loom.20110330T040437-823@post.gmane.org","subject":"Re: checkout new branch tracks wrong remote (bug?)","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-03-30T14:59:08Z","receivedAt":"2011-03-30T14:59:08Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Mar 30, 2011 at 02:27:31AM +0000, chris wrote:\n\n> I have two remotes configured.\n> \n> One is \"origin\" which has a local tracking branch \"master\" for \"origin/master\".\n> \n> The other is \"mirror\" which has option mirror = true\n> \n> While on the local branch master, I issue the command:\n> \n> $ git checkout -b wip\n> \n> The branch \"wip\" is created and oddly configured to track the \"mirror\" remote.\n\nRight. You are creating a branch from \"refs/heads/master\" (the currently\nchecked out branch). So the setup_tracking code will look for any remote\nwhich writes a tracking branch into refs/heads/master according to the\nconfiguration.\n\nYour mirror config looks like this:\n\n> [remote \"mirror\"]\n>         url = ssh://chris@myserver.com/srv/git/mirrors/chris/myproject.git\n>         fetch = +refs/*:refs/*\n>         mirror = true\n\nmeaning that a fetch of the mirror remote will write the mirror's\nrefs/heads/master into our local refs/heads/master. IOW, your master\nbranch is actually configured as a remote tracking branch of the mirror\n(which is probably not what you want; see below).\n\n> I do not expect this \"wip\" branch to be tracking the \"mirror\" remote, but rather\n> \"origin\", according to the documentation.\n\nIn the absence of the mirror remote, it would not track anything. You\nare branching from a _local_ branch, so there is no remote to track. I\nthink what you really want is:\n\n  git checkout -b wip origin/master\n\nAll of that being said, I'm not sure your config makes sense:\n\n> [remote \"origin\"]\n>         fetch = +refs/heads/*:refs/remotes/origin/*\n>         url = ssh://myserver.com/srv/git/myproject.git\n> [remote \"mirror\"]\n>         url = ssh://chris@myserver.com/srv/git/mirrors/chris/myproject.git\n>         fetch = +refs/*:refs/*\n>         mirror = true\n\nYour mirror is configured to overwrite everything in refs/ if you fetch\nfrom it. Meaning it will throw away anything you fetched from \"origin\",\nas well as any local work. So this config is probably not what you want.\n\nI'm guessing what you really wanted is a remote only for pushing to, and\ncreated it with:\n\n  git remote add --mirror mirror ssh://...\n\nThe --mirror option has problems with that case. See this thread:\n\n  http://article.gmane.org/gmane.comp.version-control.git/161653\n\nwhich has some suggestions, but nothing has been implemented yet.\nProbably it makes sense to allow --mirror=fetch and --mirror=push, but\nthere is an open question of what just \"--mirror\" should do.\n\n-Peff\n"},{"id":"164719","messageId":"20110330195139.GA814@sigill.intra.peff.net","threadId":"26918","inReplyTo":"20110330145908.GA812@sigill.intra.peff.net","subject":"[PATCH 0/3] better \"remote add --mirror\" semantics","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-03-30T19:51:39Z","receivedAt":"2011-03-30T19:51:39Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Mar 30, 2011 at 10:59:08AM -0400, Jeff King wrote:\n\n> All of that being said, I'm not sure your config makes sense:\n> \n> > [remote \"origin\"]\n> >         fetch = +refs/heads/*:refs/remotes/origin/*\n> >         url = ssh://myserver.com/srv/git/myproject.git\n> > [remote \"mirror\"]\n> >         url = ssh://chris@myserver.com/srv/git/mirrors/chris/myproject.git\n> >         fetch = +refs/*:refs/*\n> >         mirror = true\n> \n> Your mirror is configured to overwrite everything in refs/ if you fetch\n> from it. Meaning it will throw away anything you fetched from \"origin\",\n> as well as any local work. So this config is probably not what you want.\n> \n> I'm guessing what you really wanted is a remote only for pushing to, and\n> created it with:\n> \n>   git remote add --mirror mirror ssh://...\n> \n> The --mirror option has problems with that case. See this thread:\n> \n>   http://article.gmane.org/gmane.comp.version-control.git/161653\n\nHere is a patch series which I think improves the situation. +cc Jan\nHudec from the mentioned thread.\n\n  [1/3]: remote: disallow some nonsensical option combinations\n  [2/3]: remote: separate the concept of push and fetch mirrors\n  [3/3]: remote: deprecate --mirror\n\n-Peff\n"},{"id":"164720","messageId":"20110330195252.GA30624@sigill.intra.peff.net","threadId":"26918","inReplyTo":"20110330195139.GA814@sigill.intra.peff.net","subject":"[PATCH 1/3] remote: disallow some nonsensical option combinations","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-03-30T19:52:52Z","receivedAt":"2011-03-30T19:52:52Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"It doesn't make sense to use \"-m\" on a mirror, since \"-m\"\nsets up the HEAD symref in the remotes namespace, but with\nmirror, we are by definition not using a remotes namespace.\n\nSimilarly, it does not make much sense to specify refspecs\nwith --mirror. For a mirror you plan to push to, those\nrefspecs will be ignored. For a mirror you are fetching\nfrom, there is no point in mirroring, since the refspec\nspecifies everything you want to grab.\n\nThere is one case where \"--mirror -t <X>\" would be useful.\nBecause <X> is used as-is in the refspec, and because we\nappend it to to refs/, you could mirror a subset of the\nhierarchy by doing:\n\n  git remote add --mirror -t 'tags/*'\n\nBut using anything besides a single branch as an argument to\n\"-t\" is not documented and only happens to work, so closing\nit off is not a serious regression.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n builtin/remote.c |    5 +++++\n 1 files changed, 5 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex b71ecd2..2e25c6a 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -161,6 +161,11 @@ static int add(int argc, const char **argv)\n \tif (argc < 2)\n \t\tusage_with_options(builtin_remote_add_usage, options);\n \n+\tif (mirror && master)\n+\t\tdie(\"specifying a master branch makes no sense with --mirror\");\n+\tif (mirror && track.nr)\n+\t\tdie(\"specifying branches to track makes no sense with --mirror\");\n+\n \tname = argv[0];\n \turl = argv[1];\n \n-- \n1.7.4.2.8.g3ccd6\n"},{"id":"164721","messageId":"20110330195318.GB30624@sigill.intra.peff.net","threadId":"26918","inReplyTo":"20110330195139.GA814@sigill.intra.peff.net","subject":"[PATCH 2/3] remote: separate the concept of push and fetch mirrors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-03-30T19:53:19Z","receivedAt":"2011-03-30T19:53:19Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"git-remote currently has one option, \"--mirror\", which sets\nup mirror configuration which can be used for either\nfetching or pushing. It looks like this:\n\n  [remote \"mirror\"]\n    url = wherever\n    fetch = +refs/*:refs/*\n    mirror = true\n\nHowever, a remote like this can be dangerous and confusing.\nSpecifically:\n\n  1. If you issue the wrong command, it can be devastating.\n     You are not likely to \"push\" when you meant to \"fetch\",\n     but \"git remote update\" will try to fetch it, even if\n     you intended the remote only for pushing. In either\n     case, the results can be quite destructive. An\n     unintended push will overwrite or delete remote refs,\n     and an unintended fetch can overwrite local branches.\n\n  2. The tracking setup code can produce confusing results.\n     The fetch refspec above means that \"git checkout -b new\n     master\" will consider refs/heads/master to come from\n     the remote \"mirror\", even if you only ever intend to\n     push to the mirror. It will set up the \"new\" branch to\n     track mirror's refs/heads/master.\n\n  3. The push code tries to opportunistically update\n     tracking branches. If you \"git push mirror foo:bar\",\n     it will see that we are updating mirror's\n     refs/heads/bar, which corresponds to our local\n     refs/heads/bar, and will update our local branch.\n\nTo solve this, we split the concept into \"push mirrors\" and\n\"fetch mirrors\". Push mirrors set only remote.*.mirror,\nsolving (2) and (3), and making an accidental fetch write\nonly into FETCH_HEAD. Fetch mirrors set only the fetch\nrefspec, meaning an accidental push will not force-overwrite\nor delete refs on the remote end.\n\nThe new syntax is \"--mirror=<fetch|push>\". For\ncompatibility, we keep \"--mirror\" as-is, setting up both\ntypes simultaneously.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/git-remote.txt |   19 +++++++---\n builtin/remote.c             |   51 ++++++++++++++++++++-------\n t/t5505-remote.sh            |   78 ++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 129 insertions(+), 19 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 37bd3e5..28724a9 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] [--tags|--no-tags] [--mirror] <name> <url>\n+'git remote add' [-t <branch>] [-m <master>] [-f] [--tags|--no-tags] [--mirror=<fetch|push>] <name> <url>\n 'git remote rename' <old> <new>\n 'git remote rm' <name>\n 'git remote set-head' <name> (-a | -d | <branch>)\n@@ -67,11 +67,18 @@ 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+When a fetch mirror is created with `\\--mirror=fetch`, the refs will not\n+be stored in the 'refs/remotes/' namespace, but rather everything in\n+'refs/' on the remote will be directly mirrored into 'refs/' in the\n+local repository. This option only makes sense in bare repositories,\n+because a fetch would overwrite any local commits.\n++\n+When a push mirror is created with `\\--mirror=push`, then `git push`\n+will always behave as if `\\--mirror` was passed.\n++\n+The option `\\--mirror` (with no type) sets up both push and fetch\n+mirror configuration. It is kept for historical purposes, and is\n+probably not what you want.\n \n 'rename'::\n \ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 2e25c6a..570407f 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] [--mirror=<fetch|push>] <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@@ -117,6 +117,11 @@ enum {\n \tTAGS_SET = 2\n };\n \n+#define MIRROR_NONE 0\n+#define MIRROR_FETCH 1\n+#define MIRROR_PUSH 2\n+#define MIRROR_BOTH (MIRROR_FETCH|MIRROR_PUSH)\n+\n static int add_branch(const char *key, const char *branchname,\n \t\tconst char *remotename, int mirror, struct strbuf *tmp)\n {\n@@ -131,9 +136,26 @@ static int add_branch(const char *key, const char *branchname,\n \treturn git_config_set_multivar(key, tmp->buf, \"^$\", 0);\n }\n \n+static int parse_mirror_opt(const struct option *opt, const char *arg, int not)\n+{\n+\tunsigned *mirror = opt->value;\n+\tif (not)\n+\t\t*mirror = MIRROR_NONE;\n+\telse if (!arg)\n+\t\t*mirror = MIRROR_BOTH;\n+\telse if (!strcmp(arg, \"fetch\"))\n+\t\t*mirror = MIRROR_FETCH;\n+\telse if (!strcmp(arg, \"push\"))\n+\t\t*mirror = MIRROR_PUSH;\n+\telse\n+\t\treturn error(\"unknown mirror argument: %s\", arg);\n+\treturn 0;\n+}\n+\n static int add(int argc, const char **argv)\n {\n-\tint fetch = 0, mirror = 0, fetch_tags = TAGS_DEFAULT;\n+\tint fetch = 0, fetch_tags = TAGS_DEFAULT;\n+\tunsigned mirror = MIRROR_NONE;\n \tstruct string_list track = STRING_LIST_INIT_NODUP;\n \tconst char *master = NULL;\n \tstruct remote *remote;\n@@ -151,7 +173,9 @@ 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\tOPT_BOOLEAN(0, \"mirror\", &mirror, \"no separate remotes\"),\n+\t\t{ OPTION_CALLBACK, 0, \"mirror\", &mirror, \"push|fetch\",\n+\t\t\t\"set up remote as a mirror to push to or fetch from\",\n+\t\t\tPARSE_OPT_OPTARG, parse_mirror_opt },\n \t\tOPT_END()\n \t};\n \n@@ -182,18 +206,19 @@ static int add(int argc, const char **argv)\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-\tfor (i = 0; i < track.nr; i++) {\n-\t\tif (add_branch(buf.buf, track.items[i].string,\n-\t\t\t\tname, mirror, &buf2))\n-\t\t\treturn 1;\n+\tif (!mirror || mirror & MIRROR_FETCH) {\n+\t\tstrbuf_reset(&buf);\n+\t\tstrbuf_addf(&buf, \"remote.%s.fetch\", name);\n+\t\tif (track.nr == 0)\n+\t\t\tstring_list_append(&track, \"*\");\n+\t\tfor (i = 0; i < track.nr; i++) {\n+\t\t\tif (add_branch(buf.buf, track.items[i].string,\n+\t\t\t\t       name, mirror, &buf2))\n+\t\t\t\treturn 1;\n+\t\t}\n \t}\n \n-\tif (mirror) {\n+\tif (mirror & MIRROR_PUSH) {\n \t\tstrbuf_reset(&buf);\n \t\tstrbuf_addf(&buf, \"remote.%s.mirror\", name);\n \t\tif (git_config_set(buf.buf, \"true\"))\ndiff --git a/t/t5505-remote.sh b/t/t5505-remote.sh\nindex d189add..4e69c90 100755\n--- a/t/t5505-remote.sh\n+++ b/t/t5505-remote.sh\n@@ -304,6 +304,84 @@ test_expect_success 'add --mirror && prune' '\n \t git rev-parse --verify refs/heads/side)\n '\n \n+test_expect_success 'add --mirror=fetch' '\n+\tmkdir mirror-fetch &&\n+\tgit init mirror-fetch/parent &&\n+\t(cd mirror-fetch/parent &&\n+\t test_commit one) &&\n+\tgit init --bare mirror-fetch/child &&\n+\t(cd mirror-fetch/child &&\n+\t git remote add --mirror=fetch -f parent ../parent)\n+'\n+\n+test_expect_success 'fetch mirrors act as mirrors during fetch' '\n+\t(cd mirror-fetch/parent &&\n+\t git branch new &&\n+\t git branch -m master renamed\n+\t) &&\n+\t(cd mirror-fetch/child &&\n+\t git fetch parent &&\n+\t git rev-parse --verify refs/heads/new &&\n+\t git rev-parse --verify refs/heads/renamed\n+\t)\n+'\n+\n+test_expect_success 'fetch mirrors can prune' '\n+\t(cd mirror-fetch/child &&\n+\t git remote prune parent &&\n+\t test_must_fail git rev-parse --verify refs/heads/master\n+\t)\n+'\n+\n+test_expect_success 'fetch mirrors do not act as mirrors during push' '\n+\t(cd mirror-fetch/parent &&\n+\t git checkout HEAD^0\n+\t) &&\n+\t(cd mirror-fetch/child &&\n+\t git branch -m renamed renamed2 &&\n+\t git push parent\n+\t) &&\n+\t(cd mirror-fetch/parent &&\n+\t git rev-parse --verify renamed &&\n+\t test_must_fail git rev-parse --verify refs/heads/renamed2\n+\t)\n+'\n+\n+test_expect_success 'add --mirror=push' '\n+\tmkdir mirror-push &&\n+\tgit init --bare mirror-push/public &&\n+\tgit init mirror-push/private &&\n+\t(cd mirror-push/private &&\n+\t test_commit one &&\n+\t git remote add --mirror=push public ../public\n+\t)\n+'\n+\n+test_expect_success 'push mirrors act as mirrors during push' '\n+\t(cd mirror-push/private &&\n+\t git branch new &&\n+\t git branch -m master renamed &&\n+\t git push public\n+\t) &&\n+\t(cd mirror-push/private &&\n+\t git rev-parse --verify refs/heads/new &&\n+\t git rev-parse --verify refs/heads/renamed &&\n+\t test_must_fail git rev-parse --verify refs/heads/master\n+\t)\n+'\n+\n+test_expect_success 'push mirrors do not act as mirrors during fetch' '\n+\t(cd mirror-push/public &&\n+\t git branch -m renamed renamed2 &&\n+\t git symbolic-ref HEAD refs/heads/renamed2\n+\t) &&\n+\t(cd mirror-push/private &&\n+\t git fetch public &&\n+\t git rev-parse --verify refs/heads/renamed &&\n+\t test_must_fail git rev-parse --verify refs/heads/renamed2\n+\t)\n+'\n+\n test_expect_success 'add alt && prune' '\n \t(mkdir alttst &&\n \t cd alttst &&\n-- \n1.7.4.2.8.g3ccd6\n"},{"id":"164722","messageId":"20110330195339.GC30624@sigill.intra.peff.net","threadId":"26918","inReplyTo":"20110330195139.GA814@sigill.intra.peff.net","subject":"[PATCH 3/3] remote: deprecate --mirror","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-03-30T19:53:39Z","receivedAt":"2011-03-30T19:53:39Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"The configuration created by plain --mirror is dangerous and\nuseless, and we now have --mirror=fetch and --mirror=push to\nreplace it. Let's warn the user.\n\nOne alternative to this is to try to guess which type the\nuser wants. In a non-bare repository, a fetch mirror doesn't\nmake much sense, since it would overwrite local commits. But\nin a bare repository, you might use either type, or even\nboth (e.g., if you are acting as an intermediate drop-point\nacross two disconnected networks).\n\nSo rather than try for complex heuristics, let's keep it\nsimple. The user knows what they're trying to do, so let\nthem tell us.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n Documentation/git-remote.txt |    4 ----\n builtin/remote.c             |    8 +++++++-\n 2 files changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/git-remote.txt b/Documentation/git-remote.txt\nindex 28724a9..528f34a 100644\n--- a/Documentation/git-remote.txt\n+++ b/Documentation/git-remote.txt\n@@ -75,10 +75,6 @@ because a fetch would overwrite any local commits.\n +\n When a push mirror is created with `\\--mirror=push`, then `git push`\n will always behave as if `\\--mirror` was passed.\n-+\n-The option `\\--mirror` (with no type) sets up both push and fetch\n-mirror configuration. It is kept for historical purposes, and is\n-probably not what you want.\n \n 'rename'::\n \ndiff --git a/builtin/remote.c b/builtin/remote.c\nindex 570407f..8424152 100644\n--- a/builtin/remote.c\n+++ b/builtin/remote.c\n@@ -136,13 +136,19 @@ static int add_branch(const char *key, const char *branchname,\n \treturn git_config_set_multivar(key, tmp->buf, \"^$\", 0);\n }\n \n+static const char mirror_advice[] =\n+\"--mirror is dangerous and deprecated; please\\n\"\n+\"\\t use --mirror=fetch or --mirror=push instead\";\n+\n static int parse_mirror_opt(const struct option *opt, const char *arg, int not)\n {\n \tunsigned *mirror = opt->value;\n \tif (not)\n \t\t*mirror = MIRROR_NONE;\n-\telse if (!arg)\n+\telse if (!arg) {\n+\t\twarning(\"%s\", mirror_advice);\n \t\t*mirror = MIRROR_BOTH;\n+\t}\n \telse if (!strcmp(arg, \"fetch\"))\n \t\t*mirror = MIRROR_FETCH;\n \telse if (!strcmp(arg, \"push\"))\n-- \n1.7.4.2.8.g3ccd6\n"},{"id":"164726","messageId":"7vhbakmj5k.fsf@alter.siamese.dyndns.org","threadId":"26918","inReplyTo":"20110330195318.GB30624@sigill.intra.peff.net","subject":"Re: [PATCH 2/3] remote: separate the concept of push and fetch mirrors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-03-30T20:45:59Z","receivedAt":"2011-03-30T20:45:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> git-remote currently has one option, \"--mirror\", which sets\n> up mirror configuration which can be used for either\n> fetching or pushing. It looks like this:\n>\n>   [remote \"mirror\"]\n>     url = wherever\n>     fetch = +refs/*:refs/*\n>     mirror = true\n>\n> However, a remote like this can be dangerous and confusing.\n\nWhen --mirror was introduced at 3894439 (Teach \"git remote\" a mirror mode,\n2007-09-02), it was only about fetching into a bare repository from\nanother repository and there wasn't any confusion.\n\nI knew about this potential confusion when we applied 84bb2df (Add a\nremote.*.mirror configuration option, 2008-04-17), but chose to be lazy\nand ignored the issue, thinking that users are intelligent enough not to\nmix these obviously incompatible modes of operation.  If a repository is a\nmirror to fetch from somebody else, you wouldn't develop in it in the\nfirst place, and you would definitely not push it back to where you are\nmirroring from.  If a repository is mirrored into somewhere else to\npublish your work in there, you wouldn't be fetching back from there to\nobliterate your work.\n\nBeing explicit like your series does is much safer than relying on \"common\nsense\".\n\nI briefly wondered if this affects one use case where you want to\nconfigure a bare repository at your firewall boundary as a relay that\nmirrors an external public repository of somebody else by fetching and\nthen publishes that to a repository internal to your network by pushing,\nbut in that case you would have two remotes (the external --mirror=fetch\none, and the internal --mirror=push one) that are separate, so it is not\nan issue.\n\nThanks for cleaning up the two-year old mess.\n"},{"id":"164727","messageId":"20110330205734.GA2940@sigill.intra.peff.net","threadId":"26918","inReplyTo":"7vhbakmj5k.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 2/3] remote: separate the concept of push and fetch mirrors","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-03-30T20:57:34Z","receivedAt":"2011-03-30T20:57:34Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Mar 30, 2011 at 01:45:59PM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> > git-remote currently has one option, \"--mirror\", which sets\n> > up mirror configuration which can be used for either\n> > fetching or pushing. It looks like this:\n> >\n> >   [remote \"mirror\"]\n> >     url = wherever\n> >     fetch = +refs/*:refs/*\n> >     mirror = true\n> >\n> > However, a remote like this can be dangerous and confusing.\n> \n> When --mirror was introduced at 3894439 (Teach \"git remote\" a mirror mode,\n> 2007-09-02), it was only about fetching into a bare repository from\n> another repository and there wasn't any confusion.\n> \n> I knew about this potential confusion when we applied 84bb2df (Add a\n> remote.*.mirror configuration option, 2008-04-17), but chose to be lazy\n> and ignored the issue, thinking that users are intelligent enough not to\n> mix these obviously incompatible modes of operation.  If a repository is a\n> mirror to fetch from somebody else, you wouldn't develop in it in the\n> first place, and you would definitely not push it back to where you are\n> mirroring from.  If a repository is mirrored into somewhere else to\n> publish your work in there, you wouldn't be fetching back from there to\n> obliterate your work.\n> \n> Being explicit like your series does is much safer than relying on \"common\n> sense\".\n\nI think the problem is not that users lack common sense. It is that we\ngive them an option called \"--mirror\" that sets up a bogus config in\nsome circumstances. So it is the git developers who lack common sense, I\nthink. :)\n\nSpecifically, 84bb2df should not have started setting \"remote.*.mirror\",\nas it was already about fetching into a bare repository. And probably\n--mirror in a non-bare repo should have complained from the beginning.\n\nBut hey, hindsight is 20/20.\n\n> I briefly wondered if this affects one use case where you want to\n> configure a bare repository at your firewall boundary as a relay that\n> mirrors an external public repository of somebody else by fetching and\n> then publishes that to a repository internal to your network by pushing,\n> but in that case you would have two remotes (the external --mirror=fetch\n> one, and the internal --mirror=push one) that are separate, so it is not\n> an issue.\n\nExactly. I almost said in the commit message for 3/3 that you would\nnever ever want to have both remote.*.mirror set to true _and_ have\nremote.*.fetch set to \"+refs/*:refs/*\". Because by deprecating --mirror,\nwe are saying that situation is not useful.\n\nBut I really don't think it is. The only instance I could think of would\nbe a case where you have two backup repos, and you might sometimes\nmirror in one direction and sometimes in the other depending on some\nexternal factors (e.g., which one you were last able to connect to from\nsome third repo). But even then, I think I would use two separate repos.\n\nNot to mention that \"git remote\" is supposed to be a friendly helper. If\nyou want to do something crazy, you're welcome to \"git config\" it\nyourself. :)\n\n> Thanks for cleaning up the two-year old mess.\n\nNo problem. Two complaints in recent memory triggered my \"OK, I guess\nthis is biting people\" instinct. :)\n\n-Peff\n"},{"id":"164734","messageId":"7v62r0meok.fsf@alter.siamese.dyndns.org","threadId":"26918","inReplyTo":"20110330205734.GA2940@sigill.intra.peff.net","subject":"Re: [PATCH 2/3] remote: separate the concept of push and fetch mirrors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-03-30T22:22:35Z","receivedAt":"2011-03-30T22:22:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> I think the problem is not that users lack common sense. It is that we\n> give them an option called \"--mirror\" that sets up a bogus config in\n> some circumstances. So it is the git developers who lack common sense, I\n> think. :)\n>\n> Specifically, 84bb2df should not have started setting \"remote.*.mirror\",\n> as it was already about fetching into a bare repository. And probably\n> --mirror in a non-bare repo should have complained from the beginning.\n\nWhat I meant was that what 84bb2df did was sufficient for people who know\nwhich one they wanted and stuck with what they said they wanted.\n\nWhat would we call a person who first asks \"I want a push mirror to save\naway my work\" and then says \"now let's fetch from there\", without\nrealizing that such a fetch will obliterate his work?  I agree that it\nprobably is asking a bit more than \"common sense\"; it perhaps requires an\nability to think for 5 minutes what oneself is doing ;-).\n\n> But hey, hindsight is 20/20.\n\nIndeed.\n\nThanks.\n"},{"id":"164755","messageId":"loom.20110331T040801-714@post.gmane.org","threadId":"26918","inReplyTo":"7v62r0meok.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 2/3] remote: separate the concept of push and fetch mirrors","fromName":"chris","fromEmail":"jugg@hotmail.com","sentAt":"2011-03-31T02:44:49Z","receivedAt":"2011-03-31T02:44:49Z","isPatch":true,"sender":{"key":"jugg@hotmail.com","avatar":null},"body":"Junio C Hamano <gitster <at> pobox.com> writes:\n> \n> What would we call a person who first asks \"I want a push mirror to save\n> away my work\" and then says \"now let's fetch from there\", without\n> realizing that such a fetch will obliterate his work?  I agree that it\n> probably is asking a bit more than \"common sense\"; it perhaps requires an\n> ability to think for 5 minutes what oneself is doing .\n\nIf I have to stop and think for 5 minutes before I execute any git command, I \nthink there may be an issue with the tool. :)  That said - the above thoughts \nwere never the source of my surprise.\n\nThe only surprising aspect of this whole thing was the behavior of \nbranch.autosetupmerge when a 'mirror' remote existed.  Essentially the existence \nof the mirror remote turned all local branches into remote-tracking branches - \nthat is surprising.\n\nI have no issue with --mirror having its current behavior (although the proposed \nchanges certainly are more explicit and therefore clearer), however, I propose \nthat branch.autosetupmerge should ignore remotes with mirror = true.\n\nI'd also propose that when setting up a --mirror, if the repository is not bare, \nthat the fetch refs be set to \"refs/*:refs/*\" rather than \"+refs/*:refs/*\".\n\nWith those two changes, I get the functionality I want without surprises.\n\nI use the mirror for synchronizing \"local\" work between my workstations \n(home/office).  So, I use the fact that I can fetch and pull from the mirror.\n\nchris\n"},{"id":"164756","messageId":"loom.20110331T044824-341@post.gmane.org","threadId":"26918","inReplyTo":"loom.20110331T040801-714@post.gmane.org","subject":"Re: [PATCH 2/3] remote: separate the concept of push and fetch mirrors","fromName":"chris","fromEmail":"jugg@hotmail.com","sentAt":"2011-03-31T02:50:01Z","receivedAt":"2011-03-31T02:50:01Z","isPatch":true,"sender":{"key":"jugg@hotmail.com","avatar":null},"body":"chris <jugg <at> hotmail.com> writes:\n> \n> I use the mirror for synchronizing \"local\" work between my workstations \n> (home/office).  So, I use the fact that I can fetch and pull from the mirror.\n\nThat of course should say:\n\nSo, I use the fact that I can fetch from and *push* to the mirror.\n\nchris\n"},{"id":"164757","messageId":"7vfwq4kkbe.fsf@alter.siamese.dyndns.org","threadId":"26918","inReplyTo":"loom.20110331T044824-341@post.gmane.org","subject":"Re: [PATCH 2/3] remote: separate the concept of push and fetch mirrors","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-03-31T04:03:49Z","receivedAt":"2011-03-31T04:03:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"chris <jugg@hotmail.com> writes:\n\n>> I use the mirror for synchronizing \"local\" work between my workstations \n>> (home/office).  So, I use the fact that I can fetch and pull from the mirror.\n>\n> That of course should say:\n>\n> So, I use the fact that I can fetch from and *push* to the mirror.\n\nIt is not quite clear what you meant by \"mirror\" above, but I am assuming\nthat you meant that you have a third repository that you use for the sole\npurpose of synchronizing your work done in two repositories, one at home\nand the other at office.\n\nThe synchronizing point should be a normal remote in such a case.  If you\nmirror-push into the mirror from home, you may lose what you have pushed\nfrom office that you forgot to pull back to home before starting to work\nat home via the mirror.  If you mirror-fetch from the mirror from office,\nyou may lose what you worked locally on office and forgot to push out\nbefore mirror-fetching for one thing, and for another, you will be\noverwriting the tip of your current branch.\n\nUsing a pure mirror in such a three-repository situation _can_ be made to\nwork, but only if you are very careful: before you leave home, commit\neverything and push to the mirror and then go to office; when you come to\nthe office, fetch from the mirror and \"reset --hard\" before doing anything\nelse; before leaving office, commit everything and push to the mirror;\nwhen you come home, fetch from the mirror and \"reset --hard\" before doing\nanything.  Ad infinitum...\n\nHopefully we are already forbidding mirror fetching into a non-bare\nrepository, so the system is foolproofed in that direction at least to\navoid such mistakes.  I offhand do not remember if we protect the branch\nthat is currently checked out from mirror pushing, though.  Hopefully,\nreceive.denycurrentbranch will protect it, but other branches may happily\nget rewound when you do a mirror push.\n\nA safer and more customary way to set up the synchronization between two\nrepositories is to arrange them to pull from each other (and if you can\ninitiate connections only in one direction, emulate one side of \"git\nfetch\" with \"git push\").\n\nIn such an arrangement, a local branch \"master\" at home will correspond to\n\"refs/remotes/home/master\" at the office, and a local branch \"master\" at\nthe office will correspond to \"refs/remotes/office/master\" at home.  There\nis no mirror configuration involved.\n\nHopefully this will clear things up somewhat.\n"},{"id":"164789","messageId":"loom.20110331T140539-266@post.gmane.org","threadId":"26918","inReplyTo":"7vfwq4kkbe.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH 2/3] remote: separate the concept of push and fetch mirrors","fromName":"chris","fromEmail":"jugg@hotmail.com","sentAt":"2011-03-31T12:59:51Z","receivedAt":"2011-03-31T12:59:51Z","isPatch":true,"sender":{"key":"jugg@hotmail.com","avatar":null},"body":"Junio C Hamano <gitster <at> pobox.com> writes:\n> \n> chris <jugg <at> hotmail.com> writes:\n> \n> >> I use the mirror for synchronizing \"local\" work between my workstations \n> >> (home/office). So, I use the fact that I can fetch from and push to the \nmirror.\n> \n> It is not quite clear what you meant by \"mirror\" above, but I am assuming\n> that you meant that you have a third repository that you use for the sole\n> purpose of synchronizing your work done in two repositories, one at home\n> and the other at office.\n\nYes, I was referencing my original post from the top level thread that triggered \nthese patches.\n\n> The synchronizing point should be a normal remote in such a case.\n\nI find that much more cumbersome.  It is much simpler for me to generate various \npatch branches and before calling it a day/night put all of my pending changes \ninto a wip branch that isn't already on another branch and push to my mirror \nremote - all refs are pushed. No need to concern myself with ensuring I don't \nforget a newly created local ref.\n\n> If you\n> mirror-push into the mirror from home, you may lose what you have pushed\n> from office that you forgot to pull back to home before starting to work\n> at home via the mirror.\n\nIt is much more likely for me to forget to push a local ref than to forget to \nsynchronize - the point of this activity is to continue my work in a different \nlocation, something I couldn't do if I don't synchronize.  As for content in the \nmirror itself being lost - that is irrelevant, it is just a buffer.  The home \nand/or work repositories have whatever is in the mirror - fetching from the \nmirror is where fail safes, if any, are needed.\n\n> If you mirror-fetch from the mirror from office,\n> you may lose what you worked locally on office and forgot to push out\n> before mirror-fetching for one thing, and for another, you will be\n> overwriting the tip of your current branch.\n\nyes, which is the point of my second suggestion to change the fetch refs for a \nmirror remote if the local repository is not bare.  But generally, when \nintentionally fetching from a mirror I want it to overwrite whatever I have \nlocally, probably because I *had* forgotten to push from home the night before, \nand subsequently re-implemented the work at the office, so when I get home the \nfollowing night, I just blow away whatever I have locally with my work from the \noffice.  But that action certainly should be explicitly requested and not the \ndefault.\n\n> Using a pure mirror in such a three-repository situation _can_ be made to\n> work, but only if you are very careful:\n\n*careful* depends on work flow.  And a pure mirror approach works quite well for \nme in this situation, with less effort than manually managing what refs to push.\n\n> Hopefully we are already forbidding mirror fetching into a non-bare\n> repository, so the system is foolproofed in that direction at least to\n> avoid such mistakes.\n\nIf you mean what I think you mean, then you are not.\n\n>  I offhand do not remember if we protect the branch\n> that is currently checked out from mirror pushing, though.\n\nI don't know - I've only mirror pushed to a bare repository.\n\n> A safer and more customary way to set up the synchronization between two\n> repositories is to arrange them to pull from each other (and if you can\n> initiate connections only in one direction, emulate one side of \"git\n> fetch\" with \"git push\").\n\n\"customary\" or \"ideal\"?  I certainly won't argue the convenience of such a setup \nif the logistics allowed for it.\n\nOf course the most ideal way to solve this problem would be to have a laptop. In \nthe mean time I have a really useful tool called Git that generally has just \nenough rounded edges to avoid stabbing myself, but does not dumb things down to \nthe point of being controlling.  :)\n\nchris\n"}]}