{"thread":{"id":"20092","subject":"[PATCH 0/6] Tracking setup improvements: per-remote config, autosetuppush","startedAt":"2009-07-12T12:17:27Z","lastAt":"2009-07-12T21:17:43Z","messageCount":8,"participants":["Paolo Bonzini","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":6},"messages":[{"id":"117855","messageId":"1247401053-20429-1-git-send-email-bonzini@gnu.org","threadId":"20092","inReplyTo":null,"subject":"[PATCH 0/6] Tracking setup improvements: per-remote config, autosetuppush","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-12T12:17:27Z","receivedAt":"2009-07-12T12:17:27Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This patch series adds two features that are a step toward providing\nconsistent per-remote configuration of push behavior in addition\nto merge (tracking) behavior.\n\nIn particular, the two features are:\n\n1) per-remote configuration of automatic tracking setup.  This leads\nto some nice refactoring of the code handling autosetup, where the\ncombination of autosetupmerge and autosetuprebase is consolidated in\na struct.\n\n2) The ability to automatically setup push refspecs corresponding\nto local branches tracking a remote.  This is false by default, since\nwe also have push.default---and actually, a generalized default of\ntrue is probably not a good idea, unlike a per-remote setting.  In\nthe future, 'git remote add' might set the per-remote autosetuppush\nto true if it gets some appropriate command-line argument.\n\n\nPatch 1 is a somewhat tangential DWIM that I've meant to send for\na while.  It conflicts with this patch series, so I've included it.\n\nPatches 2 and 3 include the refactorings in preparation for the new\nremote.*.autosetup configuration, which is then implemented by\npatch 4.\n\nPatch 5 extracts some code to a function that will become more\ncomplicated in patch 6.  Patch 6 implements autosetuppush by\nautomatically creating and deleting push refspecs upon branch\ncreation and deletion.\n\n config: allow false and true values for branch.autosetuprebase\n branch: install_branch_config and struct tracking refactoring\n introduce a struct tracking_config\n remote: add per-remote autosetupmerge and autosetuprebase configuration\n move deletion of merge configuration to branch.c\n branch, checkout: introduce autosetuppush\n\n Documentation/config.txt |   36 +++++++++++++++++-\n branch.c                 |   92 +++++++++++++++++++++++++++++++++++++--------\n branch.h                 |    5 ++-\n builtin-branch.c         |   10 +----\n builtin-checkout.c       |    2 +-\n builtin-clone.c          |    4 +-\n cache.h                  |   11 ++++-\n config.c                 |   48 +++++++++++++++++-------\n environment.c            |    7 ++-\n remote.c                 |    3 +\n remote.h                 |    1 +\n t/t3200-branch.sh        |   92 ++++++++++++++++++++++++++++++++++++++++++++-\n 12 files changed, 259 insertions(+), 52 deletions(-)\n"},{"id":"117856","messageId":"1247401053-20429-2-git-send-email-bonzini@gnu.org","threadId":"20092","inReplyTo":"1247401053-20429-1-git-send-email-bonzini@gnu.org","subject":"[PATCH 1/6] config: allow false and true values for branch.autosetuprebase","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-12T12:17:28Z","receivedAt":"2009-07-12T12:17:28Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"Boolean values can be clearly DWIM'ed from false to never and\nfrom true to always.  This is nicer to the user because autosetupmerge\nallows boolean values.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n Documentation/config.txt |    4 ++--\n config.c                 |   12 ++++++------\n t/t3200-branch.sh        |   15 ++++++++++++---\n 3 files changed, 20 insertions(+), 11 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex cb6832b..1e37e33 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -479,12 +479,12 @@ branch.autosetuprebase::\n \tWhen a new branch is created with 'git-branch' or 'git-checkout'\n \tthat tracks another branch, this variable tells git to set\n \tup pull to rebase instead of merge (see \"branch.<name>.rebase\").\n-\tWhen `never`, rebase is never automatically set to true.\n+\tWhen `never` or `false`, rebase is never automatically set to true.\n \tWhen `local`, rebase is set to true for tracked branches of\n \tother local branches.\n \tWhen `remote`, rebase is set to true for tracked branches of\n \tremote branches.\n-\tWhen `always`, rebase will be set to true for all tracking\n+\tWhen `always` or `true`, rebase will be set to true for all tracking\n \tbranches.\n \tSee \"branch.autosetupmerge\" for details on how to set up a\n \tbranch to track another branch.\ndiff --git a/config.c b/config.c\nindex 1682273..b47a40e 100644\n--- a/config.c\n+++ b/config.c\n@@ -557,17 +557,17 @@ static int git_default_branch_config(const char *var, const char *value)\n \t}\n \tif (!strcmp(var, \"branch.autosetuprebase\")) {\n \t\tif (!value)\n-\t\t\treturn config_error_nonbool(var);\n-\t\telse if (!strcmp(value, \"never\"))\n-\t\t\tautorebase = AUTOREBASE_NEVER;\n+\t\t\tvalue = \"always\";\n+\t\tautorebase = AUTOREBASE_NEVER;\n+\t\tif (!strcmp(value, \"never\"))\n+\t\t\t;\n \t\telse if (!strcmp(value, \"local\"))\n \t\t\tautorebase = AUTOREBASE_LOCAL;\n \t\telse if (!strcmp(value, \"remote\"))\n \t\t\tautorebase = AUTOREBASE_REMOTE;\n-\t\telse if (!strcmp(value, \"always\"))\n+\t\telse if (!strcmp(value, \"always\") ||\n+\t\t\t git_config_bool (var, value))\n \t\t\tautorebase = AUTOREBASE_ALWAYS;\n-\t\telse\n-\t\t\treturn error(\"Malformed value for %s\", var);\n \t\treturn 0;\n \t}\n \ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex d59a9b4..b14a3a9 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -461,11 +461,20 @@ test_expect_success 'detect misconfigured autosetuprebase (bad value)' '\n \ttest_must_fail git branch\n '\n \n-test_expect_success 'detect misconfigured autosetuprebase (no value)' '\n+test_expect_success 'boolean value (no value) for autosetuprebase' '\n \tgit config --unset branch.autosetuprebase &&\n \techo \"[branch] autosetuprebase\" >> .git/config &&\n-\ttest_must_fail git branch &&\n-\tgit config --unset branch.autosetuprebase\n+\tgit config remote.local.url . &&\n+\tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n+\t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n+\tgit branch --track myr21 local/master &&\n+\tgit branch --track myr22 myr21 &&\n+\ttest \"$(git config branch.myr21.remote)\" = local &&\n+\ttest \"$(git config branch.myr21.merge)\" = refs/heads/master &&\n+\ttest \"z$(git config branch.myr21.rebase)\" = ztrue &&\n+\ttest \"$(git config branch.myr22.remote)\" = . &&\n+\ttest \"$(git config branch.myr22.merge)\" = refs/heads/myr21 &&\n+\ttest \"z$(git config branch.myr22.rebase)\" = ztrue\n '\n \n test_done\n-- \n1.6.2.5\n"},{"id":"117857","messageId":"1247401053-20429-3-git-send-email-bonzini@gnu.org","threadId":"20092","inReplyTo":"1247401053-20429-1-git-send-email-bonzini@gnu.org","subject":"[PATCH 2/6] branch: install_branch_config and struct tracking refactoring","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-12T12:17:29Z","receivedAt":"2009-07-12T12:17:29Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This patch combines two small refactoring.  The first is to store\na struct remote into struct tracking.  The second is to pass a\nstruct remote instead of just the name to install_branch_config.\n\nTogether, these will allow to easily implement the new configuration\nkeys remote.*.autosetupmerge and remote.*.autosetuprebase.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n branch.c        |   34 ++++++++++++++++++----------------\n branch.h        |    3 ++-\n builtin-clone.c |    4 ++--\n 3 files changed, 22 insertions(+), 19 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex 05ef3f5..d17c5ec 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -7,7 +7,7 @@\n struct tracking {\n \tstruct refspec spec;\n \tchar *src;\n-\tconst char *remote;\n+\tstruct remote *remote;\n \tint matches;\n };\n \n@@ -18,7 +18,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)\n \tif (!remote_find_tracking(remote, &tracking->spec)) {\n \t\tif (++tracking->matches == 1) {\n \t\t\ttracking->src = tracking->spec.src;\n-\t\t\ttracking->remote = remote->name;\n+\t\t\ttracking->remote = remote;\n \t\t} else {\n \t\t\tfree(tracking->spec.src);\n \t\t\tif (tracking->src) {\n@@ -32,7 +32,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)\n \treturn 0;\n }\n \n-static int should_setup_rebase(const char *origin)\n+static int should_setup_rebase(struct remote *origin)\n {\n \tswitch (autorebase) {\n \tcase AUTOREBASE_NEVER:\n@@ -47,17 +47,18 @@ static int should_setup_rebase(const char *origin)\n \treturn 0;\n }\n \n-void install_branch_config(int flag, const char *local, const char *origin, const char *remote)\n+void install_branch_config(int flag, const char *local, struct remote *remote,\n+\t\t\t   const char *merge)\n {\n \tstruct strbuf key = STRBUF_INIT;\n-\tint rebasing = should_setup_rebase(origin);\n+\tint rebasing = should_setup_rebase(remote);\n \n \tstrbuf_addf(&key, \"branch.%s.remote\", local);\n-\tgit_config_set(key.buf, origin ? origin : \".\");\n+\tgit_config_set(key.buf, remote ? remote->name : \".\");\n \n \tstrbuf_reset(&key);\n \tstrbuf_addf(&key, \"branch.%s.merge\", local);\n-\tgit_config_set(key.buf, remote);\n+\tgit_config_set(key.buf, merge);\n \n \tif (rebasing) {\n \t\tstrbuf_reset(&key);\n@@ -68,16 +69,15 @@ void install_branch_config(int flag, const char *local, const char *origin, cons\n \tif (flag & BRANCH_CONFIG_VERBOSE) {\n \t\tstrbuf_reset(&key);\n \n-\t\tstrbuf_addstr(&key, origin ? \"remote\" : \"local\");\n+\t\tstrbuf_addstr(&key, remote ? \"remote\" : \"local\");\n \n \t\t/* Are we tracking a proper \"branch\"? */\n-\t\tif (!prefixcmp(remote, \"refs/heads/\")) {\n-\t\t\tstrbuf_addf(&key, \" branch %s\", remote + 11);\n-\t\t\tif (origin)\n-\t\t\t\tstrbuf_addf(&key, \" from %s\", origin);\n-\t\t}\n+\t\tif (!prefixcmp(merge, \"refs/heads/\"))\n+\t\t\tstrbuf_addf(&key, \" branch %s\", merge + 11);\n \t\telse\n-\t\t\tstrbuf_addf(&key, \" ref %s\", remote);\n+\t\t\tstrbuf_addf(&key, \" ref %s\", merge);\n+\t\tif (remote)\n+\t\t\tstrbuf_addf(&key, \" from %s\", remote->name);\n \t\tprintf(\"Branch %s set up to track %s%s.\\n\",\n \t\t       local, key.buf,\n \t\t       rebasing ? \" by rebasing\" : \"\");\n@@ -117,9 +117,11 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,\n \t\treturn error(\"Not tracking: ambiguous information for ref %s\",\n \t\t\t\torig_ref);\n \n-\tinstall_branch_config(BRANCH_CONFIG_VERBOSE, new_ref, tracking.remote,\n-\t\t\t      tracking.src ? tracking.src : orig_ref);\n+\tif (!tracking.src)\n+\t\ttracking.src = xstrdup (orig_ref);\n \n+\tinstall_branch_config(BRANCH_CONFIG_VERBOSE, new_ref, tracking.remote,\n+\t\t\t      tracking.src);\n \tfree(tracking.src);\n \treturn 0;\n }\ndiff --git a/branch.h b/branch.h\nindex eed817a..f7c02c9 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -26,6 +26,7 @@ void remove_branch_state(void);\n  * taken from origin \"origin\".\n  */\n #define BRANCH_CONFIG_VERBOSE 01\n-extern void install_branch_config(int flag, const char *local, const char *origin, const char *remote);\n+struct remote;\n+extern void install_branch_config(int flag, const char *local, struct remote *remote, const char *merge);\n \n #endif\ndiff --git a/builtin-clone.c b/builtin-clone.c\nindex 32dea74..552ddf6 100644\n--- a/builtin-clone.c\n+++ b/builtin-clone.c\n@@ -527,7 +527,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\tremote_head = NULL;\n \t\toption_no_checkout = 1;\n \t\tif (!option_bare)\n-\t\t\tinstall_branch_config(0, \"master\", option_origin,\n+\t\t\tinstall_branch_config(0, \"master\", remote_get(option_origin),\n \t\t\t\t\t      \"refs/heads/master\");\n \t}\n \n@@ -557,7 +557,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)\n \t\t\t\t      head_points_at->peer_ref->name,\n \t\t\t\t      reflog_msg.buf);\n \n-\t\t\tinstall_branch_config(0, head, option_origin,\n+\t\t\tinstall_branch_config(0, head, remote_get(option_origin),\n \t\t\t\t\t      head_points_at->name);\n \t\t}\n \t} else if (remote_head) {\n-- \n1.6.2.5\n"},{"id":"117858","messageId":"1247401053-20429-4-git-send-email-bonzini@gnu.org","threadId":"20092","inReplyTo":"1247401053-20429-1-git-send-email-bonzini@gnu.org","subject":"[PATCH 3/6] introduce a struct tracking_config","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-12T12:17:30Z","receivedAt":"2009-07-12T12:17:30Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This struct groups together the settings for autosetupmerge and\nautosetuprebase.\n\nThis refactoring will make it easier to add per-remote tracking\nconfiguration, as well as making the addition of autosetuppush\nmore tidy.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n branch.c           |    2 +-\n builtin-branch.c   |    2 +-\n builtin-checkout.c |    2 +-\n cache.h            |    9 +++++++--\n config.c           |   34 +++++++++++++++++++++++++---------\n environment.c      |    6 ++++--\n 6 files changed, 39 insertions(+), 16 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex d17c5ec..be683d9 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -34,7 +34,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)\n \n static int should_setup_rebase(struct remote *origin)\n {\n-\tswitch (autorebase) {\n+\tswitch (git_branch_track.rebase) {\n \tcase AUTOREBASE_NEVER:\n \t\treturn 0;\n \tcase AUTOREBASE_LOCAL:\ndiff --git a/builtin-branch.c b/builtin-branch.c\nindex 5687d60..fdd6c05 100644\n--- a/builtin-branch.c\n+++ b/builtin-branch.c\n@@ -595,7 +595,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \tif (branch_use_color == -1)\n \t\tbranch_use_color = git_use_color_default;\n \n-\ttrack = git_branch_track;\n+\ttrack = git_branch_track.merge;\n \n \thead = resolve_ref(\"HEAD\", head_sha1, 0, NULL);\n \tif (!head)\ndiff --git a/builtin-checkout.c b/builtin-checkout.c\nindex 8a9a474..446cac7 100644\n--- a/builtin-checkout.c\n+++ b/builtin-checkout.c\n@@ -624,7 +624,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (opts.track == BRANCH_TRACK_UNSPECIFIED)\n-\t\topts.track = git_branch_track;\n+\t\topts.track = git_branch_track.merge;\n \tif (conflict_style) {\n \t\topts.merge = 1; /* implied */\n \t\tgit_xmerge_config(\"merge.conflictstyle\", conflict_style, NULL);\ndiff --git a/cache.h b/cache.h\nindex f1e5ede..2db4c3b 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -550,8 +550,12 @@ enum push_default_type {\n \tPUSH_DEFAULT_CURRENT,\n };\n \n-extern enum branch_track git_branch_track;\n-extern enum rebase_setup_type autorebase;\n+struct tracking_config {\n+\tenum branch_track merge;\n+\tenum rebase_setup_type rebase;\n+};\n+\n+extern struct tracking_config git_branch_track;\n extern enum push_default_type push_default;\n \n enum object_creation_mode {\n@@ -632,6 +636,7 @@ enum sharedrepo {\n \tPERM_EVERYBODY      = 0664,\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 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 b47a40e..aa695d4 100644\n--- a/config.c\n+++ b/config.c\n@@ -545,29 +545,30 @@ static int git_default_i18n_config(const char *var, const char *value)\n \treturn 0;\n }\n \n-static int git_default_branch_config(const char *var, const char *value)\n+int git_tracking_config(const char *var, const char *value, struct tracking_config *cfg)\n {\n-\tif (!strcmp(var, \"branch.autosetupmerge\")) {\n+\tvar = strrchr (var, '.');\n+\tif (!strcmp(var, \".autosetupmerge\")) {\n \t\tif (value && !strcasecmp(value, \"always\")) {\n-\t\t\tgit_branch_track = BRANCH_TRACK_ALWAYS;\n+\t\t\tcfg->merge = BRANCH_TRACK_ALWAYS;\n \t\t\treturn 0;\n \t\t}\n-\t\tgit_branch_track = git_config_bool(var, value);\n+\t\tcfg->merge = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(var, \"branch.autosetuprebase\")) {\n+\tif (!strcmp(var, \".autosetuprebase\")) {\n \t\tif (!value)\n \t\t\tvalue = \"always\";\n-\t\tautorebase = AUTOREBASE_NEVER;\n+\t\tcfg->rebase = AUTOREBASE_NEVER;\n \t\tif (!strcmp(value, \"never\"))\n \t\t\t;\n \t\telse if (!strcmp(value, \"local\"))\n-\t\t\tautorebase = AUTOREBASE_LOCAL;\n+\t\t\tcfg->rebase = AUTOREBASE_LOCAL;\n \t\telse if (!strcmp(value, \"remote\"))\n-\t\t\tautorebase = AUTOREBASE_REMOTE;\n+\t\t\tcfg->rebase = AUTOREBASE_REMOTE;\n \t\telse if (!strcmp(value, \"always\") ||\n \t\t\t git_config_bool (var, value))\n-\t\t\tautorebase = AUTOREBASE_ALWAYS;\n+\t\t\tcfg->rebase = AUTOREBASE_ALWAYS;\n \t\treturn 0;\n \t}\n \n@@ -575,6 +576,21 @@ static int git_default_branch_config(const char *var, const char *value)\n \treturn 0;\n }\n \n+static int git_default_branch_config(const char *var, const char *value)\n+{\n+\tint result;\n+\n+\tif (!prefixcmp(var, \"branch.\")\n+\t    && !strchr (var + 7, '.')) {\n+\t\tresult = git_tracking_config (var, value, &git_branch_track);\n+\t\tif (result)\n+\t\t\treturn result;\n+\t}\n+\n+\t/* Add other config variables here and to Documentation/config.txt. */\n+\treturn 0;\n+}\n+\n static int git_default_push_config(const char *var, const char *value)\n {\n \tif (!strcmp(var, \"push.default\")) {\ndiff --git a/environment.c b/environment.c\nindex 801a005..049d269 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -40,8 +40,10 @@ const char *excludes_file;\n int auto_crlf = 0;\t/* 1: both ways, -1: only when adding git objects */\n enum safe_crlf safe_crlf = SAFE_CRLF_WARN;\n unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;\n-enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;\n-enum rebase_setup_type autorebase = AUTOREBASE_NEVER;\n+struct tracking_config git_branch_track = {\n+\tBRANCH_TRACK_REMOTE,\n+\tAUTOREBASE_NEVER\n+};\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-- \n1.6.2.5\n"},{"id":"117859","messageId":"1247401053-20429-5-git-send-email-bonzini@gnu.org","threadId":"20092","inReplyTo":"1247401053-20429-1-git-send-email-bonzini@gnu.org","subject":"[PATCH 4/6] remote: add per-remote autosetupmerge and autosetuprebase configuration","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-12T12:17:31Z","receivedAt":"2009-07-12T12:17:31Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"This is self-explanatory.  I chose to retain the full range of\npossibilities for autosetuprebase, even though in practice only\nnever/false and always/true will make sense (local will be a\nsynonym of false, remote will be a synonym of true).\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n Documentation/config.txt |   13 +++++++++++++\n branch.c                 |    9 ++++++++-\n builtin-branch.c         |    4 +---\n cache.h                  |    1 +\n remote.c                 |    3 +++\n remote.h                 |    1 +\n t/t3200-branch.sh        |   41 +++++++++++++++++++++++++++++++++++++++++\n 7 files changed, 68 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 1e37e33..524a222 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -1363,6 +1363,19 @@ remote.<name>.tagopt::\n \tSetting this value to \\--no-tags disables automatic tag following when\n \tfetching from remote <name>\n \n+remote.<name>.autosetupmerge::\n+\tTells 'git-branch' and 'git-checkout' to setup new branches\n+\tso that linkgit:git-pull[1] will appropriately merge from the\n+\tstarting point branch.  If present, this overrides the global\n+\toption branch.autosetupmerge, and can have the same settings.\n+\n+remote.<name>.autosetuprebase::\n+\tWhen a new branch is created with 'git-branch' or 'git-checkout'\n+\tthat tracks another branch, this variable tells git to set\n+\tup pull to rebase instead of merge (see \"branch.<name>.rebase\").\n+\tIf present, this overrides the global option\n+\tbranch.autosetuprebase, and can have the same settings.\n+\n remotes.<group>::\n \tThe list of remotes which are fetched by \"git remote update\n \t<group>\".  See linkgit:git-remote[1].\ndiff --git a/branch.c b/branch.c\nindex be683d9..e427721 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -34,7 +34,7 @@ static int find_tracked_branch(struct remote *remote, void *priv)\n \n static int should_setup_rebase(struct remote *origin)\n {\n-\tswitch (git_branch_track.rebase) {\n+\tswitch (origin ? origin->track.rebase : git_branch_track.rebase) {\n \tcase AUTOREBASE_NEVER:\n \t\treturn 0;\n \tcase AUTOREBASE_LOCAL:\n@@ -104,6 +104,13 @@ static int setup_tracking(const char *new_ref, const char *orig_ref,\n \tif (for_each_remote(find_tracked_branch, &tracking))\n \t\treturn 1;\n \n+\tif (track == BRANCH_TRACK_UNSPECIFIED) {\n+\t\ttrack = (tracking.remote\n+\t\t\t ? tracking.remote->track.merge : git_branch_track.merge);\n+\t\tif (!track)\n+\t\t\treturn 0;\n+\t}\n+\n \tif (!tracking.matches)\n \t\tswitch (track) {\n \t\tcase BRANCH_TRACK_ALWAYS:\ndiff --git a/builtin-branch.c b/builtin-branch.c\nindex fdd6c05..f045e16 100644\n--- a/builtin-branch.c\n+++ b/builtin-branch.c\n@@ -540,7 +540,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \tint delete = 0, rename = 0, force_create = 0;\n \tint verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;\n \tint reflog = 0;\n-\tenum branch_track track;\n+\tenum branch_track track = BRANCH_TRACK_UNSPECIFIED;\n \tint kinds = REF_LOCAL_BRANCH;\n \tstruct commit_list *with_commit = NULL;\n \n@@ -595,8 +595,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix)\n \tif (branch_use_color == -1)\n \t\tbranch_use_color = git_use_color_default;\n \n-\ttrack = git_branch_track.merge;\n-\n \thead = resolve_ref(\"HEAD\", head_sha1, 0, NULL);\n \tif (!head)\n \t\tdie(\"Failed to resolve HEAD as a valid ref.\");\ndiff --git a/cache.h b/cache.h\nindex 2db4c3b..703dc45 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -881,6 +881,7 @@ extern const char *packed_object_info_detail(struct packed_git *, off_t, unsigne\n extern int update_server_info(int);\n \n 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(config_fn_t fn, void *);\ndiff --git a/remote.c b/remote.c\nindex c3ada2d..5e21da3 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -131,6 +131,7 @@ static struct remote *make_remote(const char *name, int len)\n \t\tret->name = xstrndup(name, len);\n \telse\n \t\tret->name = xstrdup(name);\n+\tret->track = git_branch_track;\n \treturn ret;\n }\n \n@@ -375,6 +376,8 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\treturn 0;\n \tremote = make_remote(name, subkey - name);\n \tremote->origin = REMOTE_CONFIG;\n+\tif (git_tracking_config(key, value, &remote->track))\n+\t\treturn -1;\n \tif (!strcmp(subkey, \".mirror\"))\n \t\tremote->mirror = git_config_bool(key, value);\n \telse if (!strcmp(subkey, \".skipdefaultupdate\"))\ndiff --git a/remote.h b/remote.h\nindex 5db8420..86b18dc 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -38,6 +38,7 @@ struct remote {\n \tint fetch_tags;\n \tint skip_default_update;\n \tint mirror;\n+\tstruct tracking_config track;\n \n \tconst char *receivepack;\n \tconst char *uploadpack;\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex b14a3a9..186ba56 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -476,5 +476,46 @@ test_expect_success 'boolean value (no value) for autosetuprebase' '\n \ttest \"$(git config branch.myr22.merge)\" = refs/heads/myr21 &&\n \ttest \"z$(git config branch.myr22.rebase)\" = ztrue\n '\n+test_expect_success 'tracking remote with branch.autosetupmerge = false' '\n+\tgit config remote.local.autosetupmerge true &&\n+\tgit config branch.autosetupmerge false &&\n+\tgit config remote.local.url . &&\n+\tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n+\t(git show-ref -q refs/remotes/local/master || git fetch local) &&\n+\tgit branch myr23 local/master &&\n+\ttest \"$(git config branch.myr23.remote)\" = local &&\n+\ttest \"$(git config branch.myr23.merge)\" = refs/heads/master\n+'\n+\n+test_expect_success 'non-tracking remote with branch.autosetupmerge = always' '\n+\tgit config remote.local.autosetupmerge false &&\n+\tgit config branch.autosetupmerge always &&\n+\tgit branch myr24 local/master &&\n+\ttest z\"$(git config branch.myr24.remote)\" = z &&\n+\ttest z\"$(git config branch.myr24.merge)\" = z\n+'\n \n+test_expect_success 'tracking branch overriding remote configuration' '\n+\tgit config remote.local.autosetupmerge false &&\n+\tgit branch --track myr25 local/master &&\n+\ttest \"$(git config branch.myr25.remote)\" = local &&\n+\ttest \"$(git config branch.myr25.merge)\" = refs/heads/master\n+'\n+\n+test_expect_success 'non-tracking branch overriding remote configuration' '\n+\tgit config remote.local.autosetupmerge true &&\n+\tgit branch --no-track myr26 local/master &&\n+\ttest z\"$(git config branch.myr26.remote)\" = z &&\n+\ttest z\"$(git config branch.myr26.merge)\" = z\n+'\n+\n+test_expect_success '--no-track and multiple matches ' '\n+\tgit config branch.autosetupmerge true &&\n+\tgit config remote.local2.url . &&\n+\tgit config remote.local2.fetch refs/heads/*:refs/remotes/local/* &&\n+\tgit fetch local2 &&\n+\tgit branch --no-track myr96 local/master\n+\ttest z\"$(git config branch.myr96.remote)\" = z &&\n+\ttest z\"$(git config branch.myr96.merge)\" = z\n+'\n test_done\n-- \n1.6.2.5\n"},{"id":"117860","messageId":"1247401053-20429-6-git-send-email-bonzini@gnu.org","threadId":"20092","inReplyTo":"1247401053-20429-1-git-send-email-bonzini@gnu.org","subject":"[PATCH 5/6] move deletion of merge configuration to branch.c","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-12T12:17:32Z","receivedAt":"2009-07-12T12:17:32Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"In the next step, deletion of merge configuration will become more\ncomplex, so we move it into a separate function.  Since the\nconfiguration is installed with a function in branch.c, I'm placing\ndeletion there as well.\n\nAt the same time, the interface is changed to get the full ref name\nincluding refs/heads/.  This makes no big difference for the caller.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n branch.c         |   11 +++++++++++\n branch.h         |    2 ++\n builtin-branch.c |    6 +-----\n 3 files changed, 14 insertions(+), 5 deletions(-)\n\ndiff --git a/branch.c b/branch.c\nindex e427721..8d3e8d8 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -85,6 +85,17 @@ void install_branch_config(int flag, const char *local, struct remote *remote,\n \tstrbuf_release(&key);\n }\n \n+void delete_branch_config (const char *name)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tif (prefixcmp(name, \"refs/heads/\"))\n+\t\treturn;\n+\tstrbuf_addf(&buf, \"branch.%s\", name + 11);\n+\tif (git_config_rename_section(buf.buf, NULL) < 0)\n+\t\twarning(\"Update of config-file failed\");\n+\tstrbuf_release(&buf);\n+}\n+\n /*\n  * This is called when new_ref is branched off of orig_ref, and tries\n  * to infer the settings for branch.<new_ref>.{remote,merge} from the\ndiff --git a/branch.h b/branch.h\nindex f7c02c9..efa4506 100644\n--- a/branch.h\n+++ b/branch.h\n@@ -21,6 +21,8 @@ void create_branch(const char *head, const char *name, const char *start_name,\n  */\n void remove_branch_state(void);\n \n+void delete_branch_config(const char *name);\n+\n /*\n  * Configure local branch \"local\" to merge remote branch \"remote\"\n  * taken from origin \"origin\".\ndiff --git a/builtin-branch.c b/builtin-branch.c\nindex f045e16..4469ea9 100644\n--- a/builtin-branch.c\n+++ b/builtin-branch.c\n@@ -166,14 +166,10 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)\n \t\t\t      bname.buf);\n \t\t\tret = 1;\n \t\t} else {\n-\t\t\tstruct strbuf buf = STRBUF_INIT;\n \t\t\tprintf(\"Deleted %sbranch %s (was %s).\\n\", remote,\n \t\t\t       bname.buf,\n \t\t\t       find_unique_abbrev(sha1, DEFAULT_ABBREV));\n-\t\t\tstrbuf_addf(&buf, \"branch.%s\", bname.buf);\n-\t\t\tif (git_config_rename_section(buf.buf, NULL) < 0)\n-\t\t\t\twarning(\"Update of config-file failed\");\n-\t\t\tstrbuf_release(&buf);\n+\t\t\tdelete_branch_config (name);\n \t\t}\n \t}\n \n-- \n1.6.2.5\n"},{"id":"117861","messageId":"1247401053-20429-7-git-send-email-bonzini@gnu.org","threadId":"20092","inReplyTo":"1247401053-20429-1-git-send-email-bonzini@gnu.org","subject":"[PATCH 6/6] branch, checkout: introduce autosetuppush","fromName":"Paolo Bonzini","fromEmail":"bonzini@gnu.org","sentAt":"2009-07-12T12:17:33Z","receivedAt":"2009-07-12T12:17:33Z","isPatch":true,"sender":{"key":"bonzini@gnu.org","avatar":"https://avatars.githubusercontent.com/u/42082?v=4"},"body":"The autosetuppush configuration arranges so that whenever\na merge configuration is setup, a push refspec is added for\nthe remote.\n\nThis is a step towards providing the functionality currently\nenabled by push.default=tracking on a per-remote basis, and\nwithout the restriction of pushing a single branch at a time.\n\nSigned-off-by: Paolo Bonzini <bonzini@gnu.org>\n---\n Documentation/config.txt |   19 +++++++++++++++++++\n branch.c                 |   38 ++++++++++++++++++++++++++++++++++++++\n cache.h                  |    1 +\n config.c                 |    4 ++++\n environment.c            |    3 ++-\n t/t3200-branch.sh        |   36 ++++++++++++++++++++++++++++++++++++\n 6 files changed, 100 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 524a222..4c27e9d 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -475,6 +475,15 @@ branch.autosetupmerge::\n \tdone when the starting point is either a local branch or remote\n \tbranch. This option defaults to true.\n \n+branch.autosetuppush::\n+\tTells 'git-branch' and 'git-checkout' to setup new branches\n+\tso that linkgit:git-push[1] will push into the starting point\n+\tbranch.  Note that this option applies only to branches created\n+\tfrom a remote branch, and only if git is setting up merging\n+\tfrom the remote branch (via any one of branch.autosetupmerge,\n+\tremote.<name>.autosetupmerge, or `--track`).  This option defaults\n+\tto false.\n+\n branch.autosetuprebase::\n \tWhen a new branch is created with 'git-branch' or 'git-checkout'\n \tthat tracks another branch, this variable tells git to set\n@@ -1369,6 +1378,16 @@ remote.<name>.autosetupmerge::\n \tstarting point branch.  If present, this overrides the global\n \toption branch.autosetupmerge, and can have the same settings.\n \n+remote.<name>.autosetuppush::\n+\tTells 'git-branch' and 'git-checkout' to setup new branches\n+\tso that linkgit:git-push[1] will push into the starting point\n+\tbranch.  If present, this overrides the global option\n+\tbranch.autosetuppush, and can have the same settings.\n+\tNote that this option applies only if git is setting\n+\tup merging from the remote branch (via any one of\n+\tbranch.autosetupmerge, remote.<name>.autosetupmerge, or\n+\t`--track`).\n+\n remote.<name>.autosetuprebase::\n \tWhen a new branch is created with 'git-branch' or 'git-checkout'\n \tthat tracks another branch, this variable tells git to set\ndiff --git a/branch.c b/branch.c\nindex 8d3e8d8..dfde568 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -51,6 +51,7 @@ void install_branch_config(int flag, const char *local, struct remote *remote,\n \t\t\t   const char *merge)\n {\n \tstruct strbuf key = STRBUF_INIT;\n+\tstruct strbuf value = STRBUF_INIT;\n \tint rebasing = should_setup_rebase(remote);\n \n \tstrbuf_addf(&key, \"branch.%s.remote\", local);\n@@ -60,6 +61,13 @@ void install_branch_config(int flag, const char *local, struct remote *remote,\n \tstrbuf_addf(&key, \"branch.%s.merge\", local);\n \tgit_config_set(key.buf, merge);\n \n+\tif (remote && remote->track.push) {\n+\t\tstrbuf_reset(&key);\n+\t\tstrbuf_addf(&key, \"remote.%s.push\", remote->name);\n+\t\tstrbuf_addf(&value, \"refs/heads/%s:%s\", local, merge);\n+\t\tgit_config_set_multivar(key.buf, value.buf, \"^$\", 0);\n+\t}\n+\n \tif (rebasing) {\n \t\tstrbuf_reset(&key);\n \t\tstrbuf_addf(&key, \"branch.%s.rebase\", local);\n@@ -83,16 +91,46 @@ void install_branch_config(int flag, const char *local, struct remote *remote,\n \t\t       rebasing ? \" by rebasing\" : \"\");\n \t}\n \tstrbuf_release(&key);\n+\tstrbuf_release(&value);\n+}\n+\n+static void strbuf_addstr_escape_re (struct strbuf *buf, const char *add)\n+{\n+\tconst char *p = add;\n+\twhile ((add = strpbrk(add, \".*?+^$(){}[]\")) != NULL) {\n+\t\tstrbuf_add(buf, p, add - p);\n+\t\tstrbuf_addf(buf, \"\\\\%c\", *add++);\n+\t\tp = add;\n+\t}\n+\tstrbuf_addstr(buf, p);\n }\n \n void delete_branch_config (const char *name)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n+\tstruct strbuf push_re = STRBUF_INIT;\n+\tstruct branch *branch;\n+\n \tif (prefixcmp(name, \"refs/heads/\"))\n \t\treturn;\n+\n+\t/* git config --unset-all remote.foo.push ^\\+?refs/heads/bar:  */\n+\tbranch = branch_get(name + 11);\n+\tstrbuf_addf(&buf, \"remote.%s.push\", branch->remote_name);\n+\tstrbuf_addstr(&push_re, \"^\\\\+?\");\n+\tstrbuf_addstr_escape_re(&push_re, name);\n+\tstrbuf_addch(&push_re, ':');\n+\tif (git_config_set_multivar(buf.buf, NULL, push_re.buf, 1) < 0) {\n+\t\twarning(\"Update of config-file failed\");\n+\t\tgoto fail;\n+\t}\n+\tstrbuf_reset(&buf);\n \tstrbuf_addf(&buf, \"branch.%s\", name + 11);\n \tif (git_config_rename_section(buf.buf, NULL) < 0)\n \t\twarning(\"Update of config-file failed\");\n+\n+fail:\n+\tstrbuf_release(&push_re);\n \tstrbuf_release(&buf);\n }\n \ndiff --git a/cache.h b/cache.h\nindex 703dc45..2d9a864 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -553,6 +553,7 @@ enum push_default_type {\n struct tracking_config {\n \tenum branch_track merge;\n \tenum rebase_setup_type rebase;\n+\tint push;\n };\n \n extern struct tracking_config git_branch_track;\ndiff --git a/config.c b/config.c\nindex aa695d4..04380bb 100644\n--- a/config.c\n+++ b/config.c\n@@ -556,6 +556,10 @@ int git_tracking_config(const char *var, const char *value, struct tracking_conf\n \t\tcfg->merge = git_config_bool(var, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(var, \".autosetuppush\")) {\n+\t\tcfg->push = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(var, \".autosetuprebase\")) {\n \t\tif (!value)\n \t\t\tvalue = \"always\";\ndiff --git a/environment.c b/environment.c\nindex 049d269..2b66ac6 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -42,7 +42,8 @@ enum safe_crlf safe_crlf = SAFE_CRLF_WARN;\n unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;\n struct tracking_config git_branch_track = {\n \tBRANCH_TRACK_REMOTE,\n-\tAUTOREBASE_NEVER\n+\tAUTOREBASE_NEVER,\n+\t0\n };\n enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED;\n #ifndef OBJECT_CREATION_MODE\ndiff --git a/t/t3200-branch.sh b/t/t3200-branch.sh\nindex 186ba56..5e23cf5 100755\n--- a/t/t3200-branch.sh\n+++ b/t/t3200-branch.sh\n@@ -233,11 +233,47 @@ test_expect_success 'avoid ambiguous track' '\n \tgit branch all1 master &&\n \ttest -z \"$(git config branch.all1.merge)\"\n '\n+test_expect_success 'test push setup' \\\n+    'git config branch.autosetupmerge true &&\n+     git config branch.autosetuppush true &&\n+     git config remote.local.url . &&\n+     git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n+     (git show-ref -q refs/remotes/local/master || git fetch local) &&\n+     git branch my11 local/master &&\n+     test $(git config branch.my11.remote) = local &&\n+     test $(git config branch.my11.merge) = refs/heads/master\n+     test $(git config remote.local.push) = refs/heads/my11:refs/heads/master\n+'\n+\n+test_expect_success 'test multiple push setups' \\\n+    'git config remote.local.url . &&\n+     git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n+     (git show-ref -q refs/remotes/local/master || git fetch local) &&\n+     git branch my12 local/master &&\n+     test $(git config branch.my12.remote) = local &&\n+     test $(git config branch.my12.merge) = refs/heads/master &&\n+     (if git config remote.local.push; then exit 1; else test $? = 2; fi)\n+'\n+\n+test_expect_success 'test push setup cleanup' \\\n+    'git config branch.autosetupmerge true &&\n+     git config branch.autosetuppush true &&\n+     git config remote.local.url . &&\n+     git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n+     (git show-ref -q refs/remotes/local/master || git fetch local) &&\n+     (git branch my11 local/master || :) &&\n+     (git branch my12 local/master || :) &&\n+     git branch -d my11 &&\n+     test $(git config remote.local.push) = refs/heads/my12:refs/heads/master &&\n+     git branch -d my12 &&\n+     test z$(git config remote.local.push) = z\n+'\n \n test_expect_success 'autosetuprebase local on a tracked local branch' '\n \tgit config remote.local.url . &&\n \tgit config remote.local.fetch refs/heads/*:refs/remotes/local/* &&\n \tgit config branch.autosetuprebase local &&\n+\tgit config branch.autosetuppush false &&\n \t(git show-ref -q refs/remotes/local/o || git fetch local) &&\n \tgit branch mybase &&\n \tgit branch --track myr1 mybase &&\n-- \n1.6.2.5\n"},{"id":"117873","messageId":"7vskh1lhxk.fsf@alter.siamese.dyndns.org","threadId":"20092","inReplyTo":"1247401053-20429-1-git-send-email-bonzini@gnu.org","subject":"Re: [PATCH 0/6] Tracking setup improvements: per-remote config, autosetuppush","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-07-12T21:17:43Z","receivedAt":"2009-07-12T21:17:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"I haven't read any of these patches, other than applying them on top of\nmaster and looking at the resulting diff in the t/ directory in the\naggregated form, and also noticing some style deviations in the C code.\n\nThe new tests not only check that the commands leave expected results in\nthe cases where these new variables are set (with or without command line\noverrides), but also seem to have checks to see if the commands behave the\nsame way as before unless the new configuration variables are used.\n\nIt is very understandable for any developers (including me) to want to\ndemonstrate that their shiny new toys work as they specified, and writing\nthe positive tests (i.e. \"does the feature kick in when the user does what\nthe manual says, and does it leave the expected result?\") is a very good\ndiscipline to protect the new features from future breakages.\n\nBut at the same time, we (again, including me) tend to forget the\nimportance about negative tests (e.g. \"does the feature refrain from\nkicking in when the user does not do what the additional part of the\nmanual says, iow, uses the traditional way of running the commands, and\ndoes it leave the expected result without the new feature's effect?\")\nbecause (1) it is rather boring, and (2) we believe too much in ourselves'\nability not to break things.\n\nSo I already am liking the series even before reading a single line of\ncode.  Makes me hope that the changes are done with the same carefulness\nas the tests ;-).\n"}]}