{"thread":{"id":"28582","subject":"[RFC/PATCH] Add multiple workdir support to branch/checkout","startedAt":"2011-10-05T03:43:24Z","lastAt":"2011-10-08T22:55:22Z","messageCount":35,"participants":["Jay Soffian","Nguyen Thai Ngoc Duy","Junio C Hamano","Andreas Krey","Jonathan Nieder","Bernhard R. Link","Jeff King","Julián Landerreche"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"176897","messageId":"1317786204-57335-1-git-send-email-jaysoffian@gmail.com","threadId":"28582","inReplyTo":null,"subject":"[RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-05T03:43:24Z","receivedAt":"2011-10-05T03:43:24Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"When using 'git new-workdir', there is no safety mechanism to prevent the\nsame branch from being checked out twice, nor to prevent a checked out\nbranch from being deleted.\n\nBy teaching 'checkout' to record the workdir path using\n'branch.<name>.checkout' when switching branches, we can easily check if a\nbranch is already checked out in another workdir before switching to that\nbranch. Similarly, we can now add a check before deleting a branch.\n\nAllow 'checkout -f' to force the checkout and issue a warning\ninstead of an error.\n\nGuard this behavior behind 'core.recordCheckouts', which we will\nteach 'git new-workdir' to set in a followup commit.\n\nNote: when switching away from a branch, we set 'branch.<name>.checkout'\nto the empty string, instead of deleting it entirely, since git_config()\notherwise leaves behind an empty section which it does not re-use.\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\n builtin/branch.c   |   10 ++++++++++\n builtin/checkout.c |   39 +++++++++++++++++++++++++++++++++++++++\n remote.c           |    4 ++++\n remote.h           |    1 +\n 4 files changed, 54 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex f49596f826..6ce1a5b133 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -182,6 +182,16 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)\n \t\t\tret = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (kinds == REF_LOCAL_BRANCH) {\n+\t\t\tstruct branch *branch = branch_get(bname.buf);\n+\t\t\tif (branch->work_tree && strlen(branch->work_tree)) {\n+\t\t\t\terror(_(\"Cannot delete the branch '%s' \"\n+\t\t\t\t\t\"which is currently checked out in '%s'\"),\n+\t\t\t\t      bname.buf, branch->work_tree);\n+\t\t\t\tret = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n \n \t\tfree(name);\n \ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5e356a6c61..26259a41a7 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -33,6 +33,7 @@ struct checkout_opts {\n \tint force_detach;\n \tint writeout_stage;\n \tint writeout_error;\n+\tint record_checkouts;\n \n \t/* not set by parse_options */\n \tint branch_exists;\n@@ -709,12 +710,35 @@ static void orphaned_commit_warning(struct commit *commit)\n \tfor_each_ref(clear_commit_marks_from_one_ref, NULL);\n }\n \n+static void record_checkout(const char *name, const char *work_tree)\n+{\n+\tstruct strbuf key = STRBUF_INIT;\n+\tstrbuf_addf(&key, \"branch.%s.checkout\", name);\n+\tgit_config_set(key.buf, work_tree);\n+\tstrbuf_release(&key);\n+}\n+\n+static void check_if_checked_out(struct checkout_opts *opts, const char *name)\n+{\n+\tstruct branch *branch = branch_get(name);\n+\tif (branch->work_tree && strlen(branch->work_tree) &&\n+\t    strcmp(branch->work_tree, get_git_work_tree())) {\n+\t\tif (opts->force)\n+\t\t\twarning(_(\"branch '%s' is currently checked out\"\n+\t\t\t\t  \" in '%s'\"), name, branch->work_tree);\n+\t\telse\n+\t\t\tdie(_(\"branch '%s' is currently checked out\"\n+\t\t\t      \" in '%s'\"), name, branch->work_tree);\n+\t}\n+}\n+\n static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n {\n \tint ret = 0;\n \tstruct branch_info old;\n \tunsigned char rev[20];\n \tint flag;\n+\n \tmemset(&old, 0, sizeof(old));\n \told.path = xstrdup(resolve_ref(\"HEAD\", rev, 0, &flag));\n \told.commit = lookup_commit_reference_gently(rev, 1);\n@@ -734,6 +758,9 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n \t\tparse_commit(new->commit);\n \t}\n \n+\tif (opts->record_checkouts)\n+\t\tcheck_if_checked_out(opts, new->name);\n+\n \tret = merge_working_tree(opts, &old, new);\n \tif (ret)\n \t\treturn ret;\n@@ -743,6 +770,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n \n \tupdate_refs_for_switch(opts, &old, new);\n \n+\tif (opts->record_checkouts) {\n+\t\tconst char *work_tree = get_git_work_tree();\n+\t\tstruct branch *branch = branch_get(old.name);\n+\t\tif (branch->work_tree && !strcmp(branch->work_tree, work_tree))\n+\t\t\trecord_checkout(old.name, \"\");\n+\t\trecord_checkout(new->name, work_tree);\n+\t}\n+\n \tret = post_checkout_hook(old.commit, new->commit, 1);\n \tfree((char *)old.path);\n \treturn ret || opts->writeout_error;\n@@ -756,6 +791,10 @@ static int git_checkout_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.recordcheckouts\")) {\n+\t\tstruct checkout_opts *opts = cb;\n+\t\topts->record_checkouts = git_config_bool(var, value);\n+\t}\n \tif (!prefixcmp(var, \"submodule.\"))\n \t\treturn parse_submodule_config_option(var, value);\n \ndiff --git a/remote.c b/remote.c\nindex b8ecfa5d95..2bc063dae8 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -364,6 +364,10 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n \t\t\tadd_merge(branch, xstrdup(value));\n+\t\t} else if (!strcmp(subkey, \".checkout\")) {\n+\t\t\tif (!value)\n+\t\t\t\treturn config_error_nonbool(key);\n+\t\t\tbranch->work_tree = xstrdup(value);\n \t\t}\n \t\treturn 0;\n \t}\ndiff --git a/remote.h b/remote.h\nindex 9a30a9dba6..4103ec7e31 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -126,6 +126,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec);\n struct branch {\n \tconst char *name;\n \tconst char *refname;\n+\tconst char *work_tree;\n \n \tconst char *remote_name;\n \tstruct remote *remote;\n-- \n1.7.7.4.g39e02c\n"},{"id":"176898","messageId":"CAG+J_Dx=65RE+QZT_r=tSPhWtGdNMhrZ-bk4A0-TtKk8WgRJZw@mail.gmail.com","threadId":"28582","inReplyTo":"1317786204-57335-1-git-send-email-jaysoffian@gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-05T03:48:22Z","receivedAt":"2011-10-05T03:48:22Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Tue, Oct 4, 2011 at 11:43 PM, Jay Soffian <jaysoffian@gmail.com> wrote:\n> When using 'git new-workdir', there is no safety mechanism to prevent the\n> same branch from being checked out twice, nor to prevent a checked out\n> branch from being deleted.\n>\n> By teaching 'checkout' to record the workdir path using\n> 'branch.<name>.checkout' when switching branches, we can easily check if a\n> branch is already checked out in another workdir before switching to that\n> branch. Similarly, we can now add a check before deleting a branch.\n>\n> Allow 'checkout -f' to force the checkout and issue a warning\n> instead of an error.\n>\n> Guard this behavior behind 'core.recordCheckouts', which we will\n> teach 'git new-workdir' to set in a followup commit.\n\nWell, depending upon what folks think of this RFC, anyway.\n\n> Note: when switching away from a branch, we set 'branch.<name>.checkout'\n> to the empty string, instead of deleting it entirely, since git_config()\n> otherwise leaves behind an empty section which it does not re-use.\n\nMaybe this is a bug in git_config()? It seems like if it's removed the\nlast item from a section, it should remove the whole section OR it\nshould re-use an empty section.\n\nj.\n"},{"id":"176899","messageId":"CACsJy8AqYq+YF+rvUp=BBeFUAtUz783iF2jbUp3fO58yLp9ptQ@mail.gmail.com","threadId":"28582","inReplyTo":"1317786204-57335-1-git-send-email-jaysoffian@gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-10-05T04:02:30Z","receivedAt":"2011-10-05T04:02:30Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Wed, Oct 5, 2011 at 2:43 PM, Jay Soffian <jaysoffian@gmail.com> wrote:\n> When using 'git new-workdir', there is no safety mechanism to prevent the\n> same branch from being checked out twice, nor to prevent a checked out\n> branch from being deleted.\n>\n> By teaching 'checkout' to record the workdir path using\n> 'branch.<name>.checkout' when switching branches, we can easily check if a\n> branch is already checked out in another workdir before switching to that\n> branch. Similarly, we can now add a check before deleting a branch.\n>\n> Allow 'checkout -f' to force the checkout and issue a warning\n> instead of an error.\n>\n> Guard this behavior behind 'core.recordCheckouts', which we will\n> teach 'git new-workdir' to set in a followup commit.\n\nI've wanted to to something like this, but you beat me to it ;)\n\nCould you please consider a more generic approach? What I have in mind\nis a mechanism to \"lock\" a branch, so that only commands that have the\nkey can update it.\n\nSo instead of branch.<name>.checkout, I would have something like\nbranch.<name>.locked = <key>, where <key> is just a string. Only\ncommands that provide the matching <key> are allowed to update the\nbranch. In checkout case, <key> could be \"checkout: worktree\".\n\nThis approach addresses more cases than just multiple workdir. We\ncould relax restrictions on pushing to a non-bare repository: we only\ndisallow pushing to locked branches. We can also use this to prevent\nusers from checking out another branch (by locking HEAD) while in the\nmiddle of interactive rebase/bisect/...\n-- \nDuy\n"},{"id":"176900","messageId":"7vmxdg9j3r.fsf@alter.siamese.dyndns.org","threadId":"28582","inReplyTo":"1317786204-57335-1-git-send-email-jaysoffian@gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-05T04:07:36Z","receivedAt":"2011-10-05T04:07:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jay Soffian <jaysoffian@gmail.com> writes:\n\n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 5e356a6c61..26259a41a7 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -709,12 +710,35 @@ static void orphaned_commit_warning(struct commit *commit)\n>  \tfor_each_ref(clear_commit_marks_from_one_ref, NULL);\n>  }\n>  \n> +static void record_checkout(const char *name, const char *work_tree)\n> +{\n> +\tstruct strbuf key = STRBUF_INIT;\n> +\tstrbuf_addf(&key, \"branch.%s.checkout\", name);\n> +\tgit_config_set(key.buf, work_tree);\n> +\tstrbuf_release(&key);\n> +}\n> +\n> +static void check_if_checked_out(struct checkout_opts *opts, const char *name)\n> +{\n> +\tstruct branch *branch = branch_get(name);\n> +\tif (branch->work_tree && strlen(branch->work_tree) &&\n> +\t    strcmp(branch->work_tree, get_git_work_tree())) {\n> +\t\tif (opts->force)\n> +\t\t\twarning(_(\"branch '%s' is currently checked out\"\n> +\t\t\t\t  \" in '%s'\"), name, branch->work_tree);\n> +\t\telse\n> +\t\t\tdie(_(\"branch '%s' is currently checked out\"\n> +\t\t\t      \" in '%s'\"), name, branch->work_tree);\n> +\t}\n> +}\n> +\n>  static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n>  {\n>  \tint ret = 0;\n>  \tstruct branch_info old;\n>  \tunsigned char rev[20];\n>  \tint flag;\n> +\n>  \tmemset(&old, 0, sizeof(old));\n>  \told.path = xstrdup(resolve_ref(\"HEAD\", rev, 0, &flag));\n>  \told.commit = lookup_commit_reference_gently(rev, 1);\n> @@ -734,6 +758,9 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n>  \t\tparse_commit(new->commit);\n>  \t}\n>  \n> +\tif (opts->record_checkouts)\n> +\t\tcheck_if_checked_out(opts, new->name);\n\nThe close brace we can see in the context closes \"if (!new->name) {\", so\nthis codepath is very well prepared to be called with new->name == NULL.\n\nIs check_if_checked_out() prepared to be called with name == NULL and do\nthe right thing?\n\n> @@ -743,6 +770,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n>  \n>  \tupdate_refs_for_switch(opts, &old, new);\n>  \n> +\tif (opts->record_checkouts) {\n> +\t\tconst char *work_tree = get_git_work_tree();\n> +\t\tstruct branch *branch = branch_get(old.name);\n> +\t\tif (branch->work_tree && !strcmp(branch->work_tree, work_tree))\n> +\t\t\trecord_checkout(old.name, \"\");\n> +\t\trecord_checkout(new->name, work_tree);\n> +\t}\n> +\n\nLikewise for new->name, but also old.name which is only set when old.path\nis set and begins with \"refs/heads/\" and otherwise NULL.\n"},{"id":"176917","messageId":"CAG+J_DygQTD5ibco=-NOiKg0BLgBGFJnvV8zPyhngC2iZv_H8g@mail.gmail.com","threadId":"28582","inReplyTo":"CACsJy8AqYq+YF+rvUp=BBeFUAtUz783iF2jbUp3fO58yLp9ptQ@mail.gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-05T13:11:47Z","receivedAt":"2011-10-05T13:11:47Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Wed, Oct 5, 2011 at 12:02 AM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n> Could you please consider a more generic approach? What I have in mind\n> is a mechanism to \"lock\" a branch, so that only commands that have the\n> key can update it.\n>\n> So instead of branch.<name>.checkout, I would have something like\n> branch.<name>.locked = <key>, where <key> is just a string. Only\n> commands that provide the matching <key> are allowed to update the\n> branch. In checkout case, <key> could be \"checkout: worktree\".\n\nIn this case, each workdir needs its own key, so I'd have to record\nthe key somewhere, unless you meant using a key of \"checkout:\n</path/to/workdir>\".\n\n> This approach addresses more cases than just multiple workdir. We\n> could relax restrictions on pushing to a non-bare repository: we only\n> disallow pushing to locked branches.\n\nIsn't that another case where you only care if the branch is checked\nout and where? So using \"branch.<name>.checkout = </path/to/workdir>\"\nshould be fine there too.\n\n> We can also use this to prevent\n> users from checking out another branch (by locking HEAD) while in the\n> middle of interactive rebase/bisect/...\n\nI dunno, that seems like a really different use case.\n\nj.\n"},{"id":"176931","messageId":"1317828285-66581-1-git-send-email-jaysoffian@gmail.com","threadId":"28582","inReplyTo":"7vmxdg9j3r.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-05T15:24:45Z","receivedAt":"2011-10-05T15:24:45Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Wed, Oct 5, 2011 at 12:07 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jay Soffian <jaysoffian@gmail.com> writes:\n>> @@ -734,6 +758,9 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n>>               parse_commit(new->commit);\n>>       }\n>>\n>> +     if (opts->record_checkouts)\n>> +             check_if_checked_out(opts, new->name);\n>\n> The close brace we can see in the context closes \"if (!new->name) {\", so\n> this codepath is very well prepared to be called with new->name == NULL.\n>\n> Is check_if_checked_out() prepared to be called with name == NULL and do\n> the right thing?\n>\n>> @@ -743,6 +770,14 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n>>\n>>       update_refs_for_switch(opts, &old, new);\n>>\n>> +     if (opts->record_checkouts) {\n>> +             const char *work_tree = get_git_work_tree();\n>> +             struct branch *branch = branch_get(old.name);\n>> +             if (branch->work_tree && !strcmp(branch->work_tree, work_tree))\n>> +                     record_checkout(old.name, \"\");\n>> +             record_checkout(new->name, work_tree);\n>> +     }\n>> +\n>\n> Likewise for new->name, but also old.name which is only set when old.path\n> is set and begins with \"refs/heads/\" and otherwise NULL.\n\nI was more looking for feedback on the idea than the implementation, but\nhere's a better implementation. Still an RFC so no tests yet.\n\n-- >8 --\nSubject: [RFC/PATCH] Teach branch/checkout about workdirs\n\nWhen using 'git new-workdir', there is no safety mechanism to prevent the\nsame branch from being checked out twice, nor to prevent a checked out\nbranch from being deleted.\n\nTeach 'checkout' to record the workdir path using 'branch.<name>.checkout'\nwhen switching branches. We can then easily check if a branch is already\nchecked out in another workdir before switching to that branch. Add a\nsimilar check before deleting a branch.\n\nAllow 'checkout -f' to force the checkout and issue a warning instead of\nan error.\n\nGuard this behavior behind 'core.recordCheckouts', which we will teach\n'git new-workdir' to set in a followup commit.\n\nNote: when switching away from a branch, we set 'branch.<name>.checkout'\nto the empty string, instead of deleting it entirely, since git_config()\notherwise leaves behind an empty section which it does not re-use.\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\n builtin/branch.c   |   10 ++++++++++\n builtin/checkout.c |   45 +++++++++++++++++++++++++++++++++++++++++++++\n remote.c           |    4 ++++\n remote.h           |    1 +\n 4 files changed, 60 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex f49596f826..6ce1a5b133 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -182,6 +182,16 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)\n \t\t\tret = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (kinds == REF_LOCAL_BRANCH) {\n+\t\t\tstruct branch *branch = branch_get(bname.buf);\n+\t\t\tif (branch->work_tree && strlen(branch->work_tree)) {\n+\t\t\t\terror(_(\"Cannot delete the branch '%s' \"\n+\t\t\t\t\t\"which is currently checked out in '%s'\"),\n+\t\t\t\t      bname.buf, branch->work_tree);\n+\t\t\t\tret = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n \n \t\tfree(name);\n \ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5e356a6c61..b3c658ffd4 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -33,6 +33,7 @@ struct checkout_opts {\n \tint force_detach;\n \tint writeout_stage;\n \tint writeout_error;\n+\tint record_checkouts;\n \n \t/* not set by parse_options */\n \tint branch_exists;\n@@ -508,6 +509,20 @@ static void detach_advice(const char *old_path, const char *new_name)\n \tfprintf(stderr, fmt, new_name);\n }\n \n+static void record_checkout(const char *name, const char *new_work_tree)\n+{\n+\tstruct strbuf key = STRBUF_INIT;\n+\tstrbuf_addf(&key, \"branch.%s.checkout\", name);\n+\tif (new_work_tree) { /* reserve name */\n+\t\tgit_config_set(key.buf, new_work_tree);\n+\t} else { /* release name if we reserved it */\n+\t\tstruct branch *branch = branch_get(name);\n+\t\tif (!strcmp(branch->work_tree, get_git_work_tree()))\n+\t\t\tgit_config_set(key.buf, \"\");\n+\t}\n+\tstrbuf_release(&key);\n+}\n+\n static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t\t   struct branch_info *old,\n \t\t\t\t   struct branch_info *new)\n@@ -556,6 +571,8 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t\tdetach_advice(old->path, new->name);\n \t\t\tdescribe_detached_head(_(\"HEAD is now at\"), new->commit);\n \t\t}\n+\t\tif (opts->record_checkouts && old->name)\n+\t\t\trecord_checkout(old->name, NULL);\n \t} else if (new->path) {\t/* Switch branches. */\n \t\tcreate_symref(\"HEAD\", new->path, msg.buf);\n \t\tif (!opts->quiet) {\n@@ -580,6 +597,11 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\tif (!file_exists(ref_file) && file_exists(log_file))\n \t\t\t\tremove_path(log_file);\n \t\t}\n+\t\tif (opts->record_checkouts) {\n+\t\t\tif (old->name)\n+\t\t\t\trecord_checkout(old->name, NULL);\n+\t\t\trecord_checkout(new->name, get_git_work_tree());\n+\t\t}\n \t}\n \tremove_branch_state();\n \tstrbuf_release(&msg);\n@@ -709,6 +731,23 @@ static void orphaned_commit_warning(struct commit *commit)\n \tfor_each_ref(clear_commit_marks_from_one_ref, NULL);\n }\n \n+static void check_if_checked_out(struct checkout_opts *opts, const char *name)\n+{\n+\tstruct branch *branch;\n+\tif (!opts->record_checkouts)\n+\t\treturn;\n+\tbranch = branch_get(name);\n+\tif (branch->work_tree && strlen(branch->work_tree) &&\n+\t    strcmp(branch->work_tree, get_git_work_tree())) {\n+\t\tif (opts->force)\n+\t\t\twarning(_(\"branch '%s' is currently checked out\"\n+\t\t\t\t  \" in '%s'\"), name, branch->work_tree);\n+\t\telse\n+\t\t\tdie(_(\"branch '%s' is currently checked out\"\n+\t\t\t      \" in '%s'\"), name, branch->work_tree);\n+\t}\n+}\n+\n static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n {\n \tint ret = 0;\n@@ -732,6 +771,8 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n \t\tif (!new->commit)\n \t\t\tdie(_(\"You are on a branch yet to be born\"));\n \t\tparse_commit(new->commit);\n+\t} else if (opts->record_checkouts) {\n+\t\tcheck_if_checked_out(opts, new->name);\n \t}\n \n \tret = merge_working_tree(opts, &old, new);\n@@ -756,6 +797,10 @@ static int git_checkout_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.recordcheckouts\")) {\n+\t\tstruct checkout_opts *opts = cb;\n+\t\topts->record_checkouts = git_config_bool(var, value);\n+\t}\n \tif (!prefixcmp(var, \"submodule.\"))\n \t\treturn parse_submodule_config_option(var, value);\n \ndiff --git a/remote.c b/remote.c\nindex b8ecfa5d95..2bc063dae8 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -364,6 +364,10 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n \t\t\tadd_merge(branch, xstrdup(value));\n+\t\t} else if (!strcmp(subkey, \".checkout\")) {\n+\t\t\tif (!value)\n+\t\t\t\treturn config_error_nonbool(key);\n+\t\t\tbranch->work_tree = xstrdup(value);\n \t\t}\n \t\treturn 0;\n \t}\ndiff --git a/remote.h b/remote.h\nindex 9a30a9dba6..4103ec7e31 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -126,6 +126,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec);\n struct branch {\n \tconst char *name;\n \tconst char *refname;\n+\tconst char *work_tree;\n \n \tconst char *remote_name;\n \tstruct remote *remote;\n-- \n1.7.7.4.g39e02c\n"},{"id":"176932","messageId":"1317830469-72878-1-git-send-email-jaysoffian@gmail.com","threadId":"28582","inReplyTo":"1317828285-66581-1-git-send-email-jaysoffian@gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-05T16:01:09Z","receivedAt":"2011-10-05T16:01:09Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"> I was more looking for feedback on the idea than the implementation, but\n> here's a better implementation. Still an RFC so no tests yet.\n\nOops. Let's try that again. Sent the wrong thing.\n\n-- >8 --\nSubject: [RFC/PATCH] Teach branch/checkout about workdirs\n\nWhen using 'git new-workdir', there is no safety mechanism to prevent the\nsame branch from being checked out twice, nor to prevent a checked out\nbranch from being deleted.\n\nTeach 'checkout' to record the workdir path using 'branch.<name>.checkout'\nwhen switching branches. We can then easily check if a branch is already\nchecked out in another workdir before switching to that branch. Add a\nsimilar check before deleting a branch.\n\nAllow 'checkout -f' to force the checkout and issue a warning instead of\nan error.\n\nGuard this behavior behind 'core.recordCheckouts', which we will teach\n'git new-workdir' to set in a followup commit.\n\nNote: when switching away from a branch, we set 'branch.<name>.checkout'\nto the empty string, instead of deleting it entirely, since git_config()\notherwise leaves behind an empty section which it does not re-use.\n\nSigned-off-by: Jay Soffian <jaysoffian@gmail.com>\n---\n builtin/branch.c   |   10 ++++++++++\n builtin/checkout.c |   43 +++++++++++++++++++++++++++++++++++++++++++\n remote.c           |    4 ++++\n remote.h           |    1 +\n 4 files changed, 58 insertions(+), 0 deletions(-)\n\ndiff --git a/builtin/branch.c b/builtin/branch.c\nindex f49596f826..6ce1a5b133 100644\n--- a/builtin/branch.c\n+++ b/builtin/branch.c\n@@ -182,6 +182,16 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)\n \t\t\tret = 1;\n \t\t\tcontinue;\n \t\t}\n+\t\tif (kinds == REF_LOCAL_BRANCH) {\n+\t\t\tstruct branch *branch = branch_get(bname.buf);\n+\t\t\tif (branch->work_tree && strlen(branch->work_tree)) {\n+\t\t\t\terror(_(\"Cannot delete the branch '%s' \"\n+\t\t\t\t\t\"which is currently checked out in '%s'\"),\n+\t\t\t\t      bname.buf, branch->work_tree);\n+\t\t\t\tret = 1;\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t}\n \n \t\tfree(name);\n \ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 5e356a6c61..75510befde 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -33,6 +33,7 @@ struct checkout_opts {\n \tint force_detach;\n \tint writeout_stage;\n \tint writeout_error;\n+\tint record_checkouts;\n \n \t/* not set by parse_options */\n \tint branch_exists;\n@@ -508,6 +509,21 @@ static void detach_advice(const char *old_path, const char *new_name)\n \tfprintf(stderr, fmt, new_name);\n }\n \n+static void record_checkout(const char *name, const char *new_work_tree)\n+{\n+\tstruct strbuf key = STRBUF_INIT;\n+\tstrbuf_addf(&key, \"branch.%s.checkout\", name);\n+\tif (new_work_tree) { /* reserve name */\n+\t\tgit_config_set(key.buf, new_work_tree);\n+\t} else { /* release name if we reserved it */\n+\t\tstruct branch *branch = branch_get(name);\n+\t\tif (branch->work_tree &&\n+\t\t    !strcmp(branch->work_tree, get_git_work_tree()))\n+\t\t\tgit_config_set(key.buf, \"\");\n+\t}\n+\tstrbuf_release(&key);\n+}\n+\n static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t\t   struct branch_info *old,\n \t\t\t\t   struct branch_info *new)\n@@ -556,6 +572,8 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\t\tdetach_advice(old->path, new->name);\n \t\t\tdescribe_detached_head(_(\"HEAD is now at\"), new->commit);\n \t\t}\n+\t\tif (opts->record_checkouts && old->name)\n+\t\t\trecord_checkout(old->name, NULL);\n \t} else if (new->path) {\t/* Switch branches. */\n \t\tcreate_symref(\"HEAD\", new->path, msg.buf);\n \t\tif (!opts->quiet) {\n@@ -580,6 +598,11 @@ static void update_refs_for_switch(struct checkout_opts *opts,\n \t\t\tif (!file_exists(ref_file) && file_exists(log_file))\n \t\t\t\tremove_path(log_file);\n \t\t}\n+\t\tif (opts->record_checkouts) {\n+\t\t\tif (old->name)\n+\t\t\t\trecord_checkout(old->name, NULL);\n+\t\t\trecord_checkout(new->name, get_git_work_tree());\n+\t\t}\n \t}\n \tremove_branch_state();\n \tstrbuf_release(&msg);\n@@ -709,6 +732,20 @@ static void orphaned_commit_warning(struct commit *commit)\n \tfor_each_ref(clear_commit_marks_from_one_ref, NULL);\n }\n \n+static void check_if_checked_out(struct checkout_opts *opts, const char *name)\n+{\n+\tstruct branch *branch = branch_get(name);\n+\tif (branch->work_tree && strlen(branch->work_tree) &&\n+\t    strcmp(branch->work_tree, get_git_work_tree())) {\n+\t\tif (opts->force)\n+\t\t\twarning(_(\"branch '%s' is currently checked out\"\n+\t\t\t\t  \" in '%s'\"), name, branch->work_tree);\n+\t\telse\n+\t\t\tdie(_(\"branch '%s' is currently checked out\"\n+\t\t\t      \" in '%s'\"), name, branch->work_tree);\n+\t}\n+}\n+\n static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n {\n \tint ret = 0;\n@@ -732,6 +769,8 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)\n \t\tif (!new->commit)\n \t\t\tdie(_(\"You are on a branch yet to be born\"));\n \t\tparse_commit(new->commit);\n+\t} else if (opts->record_checkouts) {\n+\t\tcheck_if_checked_out(opts, new->name);\n \t}\n \n \tret = merge_working_tree(opts, &old, new);\n@@ -756,6 +795,10 @@ static int git_checkout_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.recordcheckouts\")) {\n+\t\tstruct checkout_opts *opts = cb;\n+\t\topts->record_checkouts = git_config_bool(var, value);\n+\t}\n \tif (!prefixcmp(var, \"submodule.\"))\n \t\treturn parse_submodule_config_option(var, value);\n \ndiff --git a/remote.c b/remote.c\nindex b8ecfa5d95..2bc063dae8 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -364,6 +364,10 @@ static int handle_config(const char *key, const char *value, void *cb)\n \t\t\tif (!value)\n \t\t\t\treturn config_error_nonbool(key);\n \t\t\tadd_merge(branch, xstrdup(value));\n+\t\t} else if (!strcmp(subkey, \".checkout\")) {\n+\t\t\tif (!value)\n+\t\t\t\treturn config_error_nonbool(key);\n+\t\t\tbranch->work_tree = xstrdup(value);\n \t\t}\n \t\treturn 0;\n \t}\ndiff --git a/remote.h b/remote.h\nindex 9a30a9dba6..4103ec7e31 100644\n--- a/remote.h\n+++ b/remote.h\n@@ -126,6 +126,7 @@ int remote_find_tracking(struct remote *remote, struct refspec *refspec);\n struct branch {\n \tconst char *name;\n \tconst char *refname;\n+\tconst char *work_tree;\n \n \tconst char *remote_name;\n \tstruct remote *remote;\n-- \n1.7.7.5.gd207e.dirty\n"},{"id":"176936","messageId":"7vpqib8jzk.fsf@alter.siamese.dyndns.org","threadId":"28582","inReplyTo":"CAG+J_DygQTD5ibco=-NOiKg0BLgBGFJnvV8zPyhngC2iZv_H8g@mail.gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-05T16:46:07Z","receivedAt":"2011-10-05T16:46:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jay Soffian <jaysoffian@gmail.com> writes:\n\n> On Wed, Oct 5, 2011 at 12:02 AM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n>> Could you please consider a more generic approach? What I have in mind\n>> is a mechanism to \"lock\" a branch, so that only commands that have the\n>> key can update it.\n>>\n>> So instead of branch.<name>.checkout, I would have something like\n>> branch.<name>.locked = <key>, where <key> is just a string. Only\n>> commands that provide the matching <key> are allowed to update the\n>> branch. In checkout case, <key> could be \"checkout: worktree\".\n>\n> In this case, each workdir needs its own key, so I'd have to record\n> the key somewhere, unless you meant using a key of \"checkout:\n> </path/to/workdir>\".\n\nThat actually is how I read his message.\n\nI do not think \"we cannot off the top of our heads think of the reason\nother than the branch is checked out that we might want to forbid its\nupdate\" is a very good excuse to cast the word \"checkout\" in the UI; you\nwould paint yourself in a difficult corner that you have to expend more\nenergy to get out of by later adding backward compatibility support.\n\nI think \"switch_branches()\" that updates HEAD to point at a local branch\nis one good place to lock the branch, but I do not know if it is a good\nidea to hook the check into the codepaths for deletion of the branch using\n\"branch -[dD]\" and check-out of the branch using \"checkout $branch\". I\nwonder if it makes sense to add the \"checking\" hook into much lower level\nin the callchain, perhaps delete_ref(), rename_ref() and update_ref() to\ncatch attempts to update \"your\" current branch by other people. For that\nmatter, instead of switch_branches(), would it make more sense to add this\nlock/unlock logic to symbolic_ref() that repoints HEAD to other branch?\n"},{"id":"176939","messageId":"CAG+J_Dz-GXvRbYUXSoyfyHfOO-_BszcOza9x=ysHhmL5YBW-Jw@mail.gmail.com","threadId":"28582","inReplyTo":"7vpqib8jzk.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-05T17:17:13Z","receivedAt":"2011-10-05T17:17:13Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Wed, Oct 5, 2011 at 12:46 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> In this case, each workdir needs its own key, so I'd have to record\n>> the key somewhere, unless you meant using a key of \"checkout:\n>> </path/to/workdir>\".\n>\n> That actually is how I read his message.\n>\n> I do not think \"we cannot off the top of our heads think of the reason\n> other than the branch is checked out that we might want to forbid its\n> update\" is a very good excuse to cast the word \"checkout\" in the UI; you\n> would paint yourself in a difficult corner that you have to expend more\n> energy to get out of by later adding backward compatibility support.\n\nGit has survived w/o needing to lock branches till now. What are these\nuse cases we cannot already think of today?\n\n> I think \"switch_branches()\" that updates HEAD to point at a local branch\n> is one good place to lock the branch, but I do not know if it is a good\n> idea to hook the check into the codepaths for deletion of the branch using\n> \"branch -[dD]\" and check-out of the branch using \"checkout $branch\". I\n> wonder if it makes sense to add the \"checking\" hook into much lower level\n> in the callchain, perhaps delete_ref(), rename_ref() and update_ref() to\n> catch attempts to update \"your\" current branch by other people.\n\nI don't think so. There are lots of ways to shoot yourself in the foot\nat the plumbing level. Besides, this is not about all refs, just local\nbranches.\n\nAside, there's nothing wrong with renaming a checked out branch.\n\n> For that\n> matter, instead of switch_branches(), would it make more sense to add this\n> lock/unlock logic to symbolic_ref() that repoints HEAD to other branch?\n\nI think you mean create_symref()? Looking at it's callers that seems\ntoo low-level.\n\nMaybe you could sketch out how you think this should work, I'm not seeing it.\n\n- Where/how should the lock be recorded?\n- Which function(s) should record/release the lock?\n\nj.\n"},{"id":"176949","messageId":"7vzkhf713u.fsf@alter.siamese.dyndns.org","threadId":"28582","inReplyTo":"CAG+J_Dz-GXvRbYUXSoyfyHfOO-_BszcOza9x=ysHhmL5YBW-Jw@mail.gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-05T18:19:17Z","receivedAt":"2011-10-05T18:19:17Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jay Soffian <jaysoffian@gmail.com> writes:\n\n> Git has survived w/o needing to lock branches till now.\n\nCareful. Git has survived without your patch series till now, as people\nlearned to be careful when they use separate workdirs and avoid certain\nthings, to the point that they are not necessarily aware that they are\navoiding them (one good practice is to keep the HEADs of non-primary\nworkdirs detached).\n\nDoes that mean what your patch aims to do is unnecessary? I think not.\n\n> What are these\n> use cases we cannot already think of today?\n\nWhat is important is that we should have learned by now that the \"gotchas\"\nlive where we do not immediately see. \"Can you tell me what you are missing?\"\nis a senseless thing to ask.\n\n>> I think \"switch_branches()\" that updates HEAD to point at a local branch\n>> is one good place to lock the branch, but I do not know if it is a good\n>> idea to hook the check into the codepaths for deletion of the branch using\n>> \"branch -[dD]\" and check-out of the branch using \"checkout $branch\". I\n>> wonder if it makes sense to add the \"checking\" hook into much lower level\n>> in the callchain, perhaps delete_ref(), rename_ref() and update_ref() to\n>> catch attempts to update \"your\" current branch by other people.\n>\n> I don't think so. There are lots of ways to shoot yourself in the foot\n> at the plumbing level. Besides, this is not about all refs, just local\n> branches.\n>\n> Aside, there's nothing wrong with renaming a checked out branch.\n\nThere are pros and cons between hooking at lower level vs higher\nlevel. The advantage of hooking at higher level is you do not risk\nbreaking low-level operations, but that directly results in allowing the\nsame low level operations that are unaware of the new requirement higher\nlevel added break it. It also allows other high level operations you\nforgot to teach the new requirement break it.\n\nFor example, you checkout branch frotz in a workdir, and then in the\nprimary repository that has nitfol branch checked out, you rename the\nfrotz branch to xyzzy. The HEAD of workdir still says refs/heads/frotz\nthat no longer exist. Of course you can break the same way by doing a\n\"update-ref -d refs/heads/frotz\" from the primary repository.\n\nBecause you forgot that the high level operation \"branch renaming\" needs\nto be aware of that \"this branch is checked out elsewhere\" information,\nyou allowed it to break the workdir. If you hooked into lower level\nmachinery that is shared, you wouldn't have caused this breakage.\nSimilarly, if delete_ref() were taught about the new requirement, you\nwould have covered both \"branch -d\" and \"update-ref -d\".\n\nI do not necessarily think that it is a good approach to forbid the same\nbranch to be checked out in two different places, by the way. One reason\npeople would want to keep multiple workdirs is so that while they are\nstill working on a branch and are not yet at a good \"stop point\" to even\nmake a temporary commit to get interrupted, they find it sometimes\nnecessary to be able to build the tip of that same branch and even make a\nsmall in-working-tree fixes (which later will be carried back to the\nprimary branch). The problem arises only when one of the repositories try\nto update or delete the branch while it is checked out in another working\ntree.\n\nCan this series be extended/reworked so that:\n\n - Each branch has multi-value configuration record to note the workdirs\n   that it is checked out;\n\n - Error out (or warn if forced) upon any attempt to update the tip of a\n   branch that is checked out in more than one place; and\n\n - Similarly for renaming or deleting a branch that is checked out in more\n   than one place.\n   \n"},{"id":"176952","messageId":"CAG+J_Dzg2D+vmFRfLX01S2k98YZQBE0FFv76VAyPnXdetyWADQ@mail.gmail.com","threadId":"28582","inReplyTo":"7vzkhf713u.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-05T19:11:30Z","receivedAt":"2011-10-05T19:11:30Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Wed, Oct 5, 2011 at 2:19 PM, Junio C Hamano <gitster@pobox.com> wrote:\n\n> Careful. Git has survived without your patch series till now, as people\n> learned to be careful when they use separate workdirs and avoid certain\n> things, to the point that they are not necessarily aware that they are\n> avoiding them (one good practice is to keep the HEADs of non-primary\n> workdirs detached).\n\nI think it's more likely the case that most people have avoided\nnew-workdir entirely.\n\nAlso, while I might recommend new-workdir to my coworkers with the\nadvice \"don't checkout the same branch in multiple workdirs\", never in\na million years would I say \"use new-workdir, but make sure to only\nuse a detached HEAD in the workdirs.\" The latter would make their\nactual HEADs explode. :-)\n\n> For example, you checkout branch frotz in a workdir, and then in the\n> primary repository that has nitfol branch checked out, you rename the\n> frotz branch to xyzzy. The HEAD of workdir still says refs/heads/frotz\n> that no longer exist. Of course you can break the same way by doing a\n> \"update-ref -d refs/heads/frotz\" from the primary repository.\n>\n> Because you forgot that the high level operation \"branch renaming\" needs\n> to be aware of that \"this branch is checked out elsewhere\" information,\n> you allowed it to break the workdir. If you hooked into lower level\n> machinery that is shared, you wouldn't have caused this breakage.\n> Similarly, if delete_ref() were taught about the new requirement, you\n> would have covered both \"branch -d\" and \"update-ref -d\".\n\nI did not forget, I just hadn't gotten there yet while this was still\nan RFC/PATCH.\n\nAnother issue to resolve is what happens when the workdir or repo are\nmoved in the filesystem. And making prune aware of HEAD reflogs in the\nalternate workdirs.\n\n> I do not necessarily think that it is a good approach to forbid the same\n> branch to be checked out in two different places, by the way. One reason\n> people would want to keep multiple workdirs is so that while they are\n> still working on a branch and are not yet at a good \"stop point\" to even\n> make a temporary commit to get interrupted, they find it sometimes\n> necessary to be able to build the tip of that same branch and even make a\n> small in-working-tree fixes (which later will be carried back to the\n> primary branch). The problem arises only when one of the repositories try\n> to update or delete the branch while it is checked out in another working\n> tree.\n\nThat is not at all my experience of how workdirs are used.\n\n> Can this series be extended/reworked so that:\n>\n>  - Each branch has multi-value configuration record to note the workdirs\n>   that it is checked out;\n\nThis is a good idea in any case for when \"checkout --force\" is used\n(see below), so that we can find all the workdirs for other operations\nthat may need to.\n\n>  - Error out (or warn if forced) upon any attempt to update the tip of a\n>   branch that is checked out in more than one place; and\n\nI think that's a worse user experience. \"Sorry, can't commit your\nchanges because you've checked out this branch elsewhere.\" Now the\nuser's choices are:\n\n1. commit --force (and thus confusing the other workdirs)\n2. checkout -b new_branch && commit\n\nBoth of which I think are worse than preventing the checkout in the first place.\n\n>  - Similarly for renaming or deleting a branch that is checked out in more\n>   than one place.\n\nYep.\n\nj.\n"},{"id":"176954","messageId":"CAG+J_DxqW5J01VNe7c86SMSZPWuz=cuFJm4PaeOvr4dnQryrwQ@mail.gmail.com","threadId":"28582","inReplyTo":"7vzkhf713u.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-05T19:14:08Z","receivedAt":"2011-10-05T19:14:08Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"Aside, previous discussion -\nhttp://thread.gmane.org/gmane.comp.version-control.git/150559\n\nSadly, it seems to have petered out, it looks to me like a case of\nperfect being the enemy of the good. I'm really just trying to make it\ngood enough that we can move new-workdir out of contrib. It's a\nvaluable tool, we just need to remove some of its sharper edges.\n\nj.\n"},{"id":"176955","messageId":"20111005200043.GA32732@inner.h.iocl.org","threadId":"28582","inReplyTo":"CAG+J_Dzg2D+vmFRfLX01S2k98YZQBE0FFv76VAyPnXdetyWADQ@mail.gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Andreas Krey","fromEmail":"a.krey@gmx.de","sentAt":"2011-10-05T20:00:43Z","receivedAt":"2011-10-05T20:00:43Z","isPatch":true,"sender":{"key":"a.krey@gmx.de","avatar":"https://avatars.githubusercontent.com/u/37810?v=4"},"body":"On Wed, 05 Oct 2011 15:11:30 +0000, Jay Soffian wrote:\n...\n> >  - Error out (or warn if forced) upon any attempt to update the tip of a\n> >   branch that is checked out in more than one place; and\n> \n> I think that's a worse user experience. \"Sorry, can't commit your\n> changes because you've checked out this branch elsewhere.\"\n\nThis is actually pretty much the same as \"you can't push into the\ncurrently checked-out branch\".\n\nI do come from CVS where multiple checkouts of the same branch are obviously\ncommon, but the semantics are different. git would need to allow to be in\na detached state but still have a notion of a 'current' branch to mimic that;\nthis tentative 'current' branch being what we need to merge or rebase onto later.\nJust thinking.\n\nIt may actually be logical to put the other workdirs into detached state when\nthe branch they are on is committed into; however, this is seriously confusing.\n\n> Now the\n> user's choices are:\n> \n> 1. commit --force (and thus confusing the other workdirs)\n> 2. checkout -b new_branch && commit\n> \n> Both of which I think are worse than preventing the checkout in the first place.\n\nHmm. You mean forcing the user to make a new branch *earlier* than at\ncommit time is better?\n\nAndreas\n"},{"id":"176959","messageId":"CAG+J_DynQ8U6T9YMsWstKF_Cf6CSCr8b8E4T=p5uyGPh28G=kA@mail.gmail.com","threadId":"28582","inReplyTo":"20111005200043.GA32732@inner.h.iocl.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-05T20:50:45Z","receivedAt":"2011-10-05T20:50:45Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Wed, Oct 5, 2011 at 4:00 PM, Andreas Krey <a.krey@gmx.de> wrote:\n> Hmm. You mean forcing the user to make a new branch *earlier* than at\n> commit time is better?\n\nIn my mind, we're trying to make new-workdir usable for non-advanced\nusers. I think it's conceptually simplest to allow a branch to be\nchecked out only once.\n\nFWIW, I use a modified copy of new-workdir w/this usage:\n\n  git new-workdir <repo> <workdir> <ref> [<start>]\n\nWhich allows me to create a new branch and workdir checked out to the\nnew branch in one shot. It refuses to create the <workdir> if <ref>\nresolves to a checked-out branch. (If I want to start detached I can\ndo so with <ref>^0, but I rarely if ever do that.)\n\nj.\n"},{"id":"176961","messageId":"7vy5wz5dql.fsf@alter.siamese.dyndns.org","threadId":"28582","inReplyTo":"CAG+J_Dzg2D+vmFRfLX01S2k98YZQBE0FFv76VAyPnXdetyWADQ@mail.gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-05T21:29:22Z","receivedAt":"2011-10-05T21:29:22Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jay Soffian <jaysoffian@gmail.com> writes:\n\n> On Wed, Oct 5, 2011 at 2:19 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>\n> Also, while I might recommend new-workdir to my coworkers with the\n> advice \"don't checkout the same branch in multiple workdirs\", never in\n> a million years would I say \"use new-workdir, but make sure to only\n> use a detached HEAD in the workdirs.\" The latter would make their\n> actual HEADs explode. :-)\n>\n>> ...\n>> Because you forgot that the high level operation \"branch renaming\" needs\n>> to be aware of that \"this branch is checked out elsewhere\" information,\n>> you allowed it to break the workdir. If you hooked into lower level\n>> machinery that is shared, you wouldn't have caused this breakage.\n>> Similarly, if delete_ref() were taught about the new requirement, you\n>> would have covered both \"branch -d\" and \"update-ref -d\".\n>\n> I did not forget, I just hadn't gotten there yet while this was still\n> an RFC/PATCH.\n\nYou conveniently also forgot that you also said:\n\n> Aside, there's nothing wrong with renaming a checked out branch.\n\nWith that lack of understanding, it wasn't \"hadn't gotten there\", but was\nactually \"didn't even know it was needed\".\n\nBut that is OK. We have discussions to find out what we missed by learning\nfrom others' insights.\n\n> Another issue to resolve is what happens when the workdir or repo are\n> moved in the filesystem. And making prune aware of HEAD reflogs in the\n> alternate workdirs.\n>\n>> I do not necessarily think that it is a good approach to forbid the same\n>> branch to be checked out in two different places, by the way. One reason\n>> people would want to keep multiple workdirs is so that while they are\n>> still working on a branch and are not yet at a good \"stop point\" to even\n>> make a temporary commit to get interrupted, they find it sometimes\n>> necessary to be able to build the tip of that same branch and even make a\n>> small in-working-tree fixes (which later will be carried back to the\n>> primary branch). The problem arises only when one of the repositories try\n>> to update or delete the branch while it is checked out in another working\n>> tree.\n>\n> That is not at all my experience of how workdirs are used.\n\nI am afraid to say, with that statement, that your knowledge about the way\nthe tool can be used is not wide enough to judge if one policy restriction\n(e.g. \"never check out the same branch in multiple places\") is general\nenough to add to the tool. I do not claim mine is good enough, but I at\nleast know better than proposing a rule that may be too restictive to\nnegatively affect other people's workflows.\n\nI always maintain four workdirs that I can use to build the tip of four\nintegration branches while I work on other things in the primary branch,\nplus another that has a detached HEAD so that I can \"reset --hard\" around\nwithout having to worry about what I do there would negatively affect what\nI do elsewhere or vice versa. Of course, updating 'master' in my primary\nrepository will require the \"build master\" workdir to be \"reset --hard\"\nbefore it is used, and that is part of my workflow already. I consider it\nis one of \"people learned to work around the restriction of the tool so\nwell already that they may not realize it\" we discussed earlier.\n\nAlso, if your goal is to give a different semantics, like:\n\n> In my mind, we're trying to make new-workdir usable for non-advanced\n> users. I think it's conceptually simplest to allow a branch to be\n> checked out only once.\n\nyou would really need to make sure that your changes would not harm other\nusers of the same tool that you are not targetting for, and also the\nchanges to the core part of the system that needs your specialized policy\nmakes sense in the wider context. The former you can claim \"the policy\ndoes not kick in when configuration is not set\", but that is weak if the\npolicy is too ad-hoc and not well thought out. I actually care about the\nlatter more, as it is not worth having to spend maintenance effort to\ncarry changes that only stop some but not other kind of mistakes in the\ncore part to be more widely applicable.\n"},{"id":"176963","messageId":"20111005213002.GA12667@elie","threadId":"28582","inReplyTo":"CAG+J_DynQ8U6T9YMsWstKF_Cf6CSCr8b8E4T=p5uyGPh28G=kA@mail.gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-10-05T21:30:02Z","receivedAt":"2011-10-05T21:30:02Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Jay Soffian wrote:\n\n> In my mind, we're trying to make new-workdir usable for non-advanced\n> users.\n\nI'd be happy already with making it comfortable for the advanced\nusers. :)\n\nI think your patch goes in a right direction (using the shared\n.git/config file as a way to negotiate ownership of branches).\nJunio’s comments about it seeming sensible to\n\n - make this apply to other operations that clobber a branch\n - make the “[branch \"master\"] checkedout” configuration multi-valued\n   if there is to be support for \"git checkout -f\" overriding this at\n   all\n\nring true to me.  Making the value of this variable the path to the\n.git dir or worktree (rather than an opaque string) seems like a very\ngood thing: it means that a future git could check if the directory\nstill exists and break the lock if someone has used “rm -fr”.\n\nAs for moving “git new-workdir” out of contrib, I believe another\nprerequisite is sharing the HEAD reflog.\n\nJust my two cents,\nJonathan\n"},{"id":"176965","messageId":"CAG+J_DzU4wfbur9kBx+hYoePD9bM=Qy5zDyZX=GXi+G68X=64w@mail.gmail.com","threadId":"28582","inReplyTo":"7vy5wz5dql.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-05T21:49:02Z","receivedAt":"2011-10-05T21:49:02Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Wed, Oct 5, 2011 at 5:29 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> I am afraid to say, with that statement, that your knowledge about the way\n> the tool can be used is not wide enough to judge if one policy restriction\n> (e.g. \"never check out the same branch in multiple places\") is general\n> enough to add to the tool. I do not claim mine is good enough, but I at\n> least know better than proposing a rule that may be too restictive to\n> negatively affect other people's workflows.\n>\n> I always maintain four workdirs that I can use to build the tip of four\n> integration branches while I work on other things in the primary branch,\n> plus another that has a detached HEAD so that I can \"reset --hard\" around\n> without having to worry about what I do there would negatively affect what\n> I do elsewhere or vice versa. Of course, updating 'master' in my primary\n> repository will require the \"build master\" workdir to be \"reset --hard\"\n> before it is used, and that is part of my workflow already. I consider it\n> is one of \"people learned to work around the restriction of the tool so\n> well already that they may not realize it\" we discussed earlier.\n\nIs it a regression to your workflow that you have to now use \"checkout\n-f\" instead of \"checkout\" to checkout the same branch in more than one\nlocation?\n\n> Also, if your goal is to give a different semantics, like:\n>\n>> In my mind, we're trying to make new-workdir usable for non-advanced\n>> users. I think it's conceptually simplest to allow a branch to be\n>> checked out only once.\n>\n> you would really need to make sure that your changes would not harm other\n> users of the same tool that you are not targetting for, and also the\n> changes to the core part of the system that needs your specialized policy\n> makes sense in the wider context. The former you can claim \"the policy\n> does not kick in when configuration is not set\", but that is weak if the\n> policy is too ad-hoc and not well thought out. I actually care about the\n> latter more, as it is not worth having to spend maintenance effort to\n> carry changes that only stop some but not other kind of mistakes in the\n> core part to be more widely applicable.\n\nPerhaps:\n\n  core.multipleCheckouts = {true,false}\n    - false prevents multiple checkouts without -f\n  advice.multipleCheckouts = {true,false}\n    - false disables multiple-checkout notice\n\nAnd when there are multiple checkouts, warn on committing about other\nworkdirs that now need reset --hard.\n\nj.\n"},{"id":"176966","messageId":"CAG+J_Dz=9jAFBQ5fpY=d6M5Zc-BhNFi6foKJx69v3n3Km-U0rg@mail.gmail.com","threadId":"28582","inReplyTo":"20111005213002.GA12667@elie","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-05T21:52:23Z","receivedAt":"2011-10-05T21:52:23Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Wed, Oct 5, 2011 at 5:30 PM, Jonathan Nieder <jrnieder@gmail.com> wrote:\n> As for moving “git new-workdir” out of contrib, I believe another\n> prerequisite is sharing the HEAD reflog.\n\nI don't understand this. Is it about not gc'ing commits that other\nworkdirs are detached on, or something more?\n\nI like that each of my workdirs have their own HEAD reflog.\n\nj.\n"},{"id":"176970","messageId":"20111005215742.GB12747@elie","threadId":"28582","inReplyTo":"CAG+J_Dz=9jAFBQ5fpY=d6M5Zc-BhNFi6foKJx69v3n3Km-U0rg@mail.gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2011-10-05T21:57:42Z","receivedAt":"2011-10-05T21:57:42Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Jay Soffian wrote:\n\n> I don't understand this. Is it about not gc'ing commits that other\n> workdirs are detached on, or something more?\n>\n> I like that each of my workdirs have their own HEAD reflog.\n\nYes, sorry for the lack of clarity.  I only meant that \"git gc\" needs\nto be aware of the HEAD reflog for other workdirs (e.g., as described\nin the thread following madcoder's proposal), not that it would be a\ngood idea for the reflogs to actually be symlinked.\n"},{"id":"176974","messageId":"CACsJy8D9xgLtYTkgWWkiuQPbonfM7zY49WDxaW9ng=e7x_Pk5g@mail.gmail.com","threadId":"28582","inReplyTo":"7vpqib8jzk.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-10-05T22:38:52Z","receivedAt":"2011-10-05T22:38:52Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Oct 6, 2011 at 3:46 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jay Soffian <jaysoffian@gmail.com> writes:\n>\n>> On Wed, Oct 5, 2011 at 12:02 AM, Nguyen Thai Ngoc Duy <pclouds@gmail.com> wrote:\n>>> Could you please consider a more generic approach? What I have in mind\n>>> is a mechanism to \"lock\" a branch, so that only commands that have the\n>>> key can update it.\n>>>\n>>> So instead of branch.<name>.checkout, I would have something like\n>>> branch.<name>.locked = <key>, where <key> is just a string. Only\n>>> commands that provide the matching <key> are allowed to update the\n>>> branch. In checkout case, <key> could be \"checkout: worktree\".\n>>\n>> In this case, each workdir needs its own key, so I'd have to record\n>> the key somewhere, unless you meant using a key of \"checkout:\n>> </path/to/workdir>\".\n>\n> That actually is how I read his message.\n\nThat's what I meant.\n\n> I think \"switch_branches()\" that updates HEAD to point at a local branch\n> is one good place to lock the branch, but I do not know if it is a good\n> idea to hook the check into the codepaths for deletion of the branch using\n> \"branch -[dD]\" and check-out of the branch using \"checkout $branch\". I\n> wonder if it makes sense to add the \"checking\" hook into much lower level\n> in the callchain, perhaps delete_ref(), rename_ref() and update_ref() to\n> catch attempts to update \"your\" current branch by other people.\n\nI'd aim at low-level ref manipulation because too me it affects more\nthan just \"git checkout\".\n\n> For that\n> matter, instead of switch_branches(), would it make more sense to add this\n> lock/unlock logic to symbolic_ref() that repoints HEAD to other branch?\n\nCouldn't find symbolic_ref() in current code. If you meant\ncreate_symref(), yes that would make sense.\n-- \nDuy\n"},{"id":"176975","messageId":"CACsJy8BHeZZqsOP_+OSPfrPdkYgKQe3LgaGfo3bERD+hWT7U0g@mail.gmail.com","threadId":"28582","inReplyTo":"7vzkhf713u.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-10-05T22:47:11Z","receivedAt":"2011-10-05T22:47:11Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Oct 6, 2011 at 5:19 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> I do not necessarily think that it is a good approach to forbid the same\n> branch to be checked out in two different places, by the way. One reason\n> people would want to keep multiple workdirs is so that while they are\n> still working on a branch and are not yet at a good \"stop point\" to even\n> make a temporary commit to get interrupted, they find it sometimes\n> necessary to be able to build the tip of that same branch and even make a\n> small in-working-tree fixes (which later will be carried back to the\n> primary branch). The problem arises only when one of the repositories try\n> to update or delete the branch while it is checked out in another working\n> tree.\n\nI think of two options:\n\n - detach from the already locked branch (pretty much like what we do\nwith tags now)\n\n - refuse normally but let \"checkout -f\" do it anyway. However the\ncheckout lock will remain at the original worktree. If you want to\nupdate branch from the second checkout, do \"commit -f\" and take\nresponsibility for your action.\n-- \nDuy\n"},{"id":"176976","messageId":"7vaa9f59p5.fsf@alter.siamese.dyndns.org","threadId":"28582","inReplyTo":"CACsJy8BHeZZqsOP_+OSPfrPdkYgKQe3LgaGfo3bERD+hWT7U0g@mail.gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-05T22:56:38Z","receivedAt":"2011-10-05T22:56:38Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> On Thu, Oct 6, 2011 at 5:19 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>> I do not necessarily think that it is a good approach to forbid the same\n>> branch to be checked out in two different places, by the way. One reason\n>> people would want to keep multiple workdirs is so that while they are\n>> still working on a branch and are not yet at a good \"stop point\" to even\n>> make a temporary commit to get interrupted, they find it sometimes\n>> necessary to be able to build the tip of that same branch and even make a\n>> small in-working-tree fixes (which later will be carried back to the\n>> primary branch). The problem arises only when one of the repositories try\n>> to update or delete the branch while it is checked out in another working\n>> tree.\n>\n> I think of two options:\n>\n>  - detach from the already locked branch (pretty much like what we do\n> with tags now)\n>\n>  - refuse normally but let \"checkout -f\" do it anyway. However the\n> checkout lock will remain at the original worktree. If you want to\n> update branch from the second checkout, do \"commit -f\" and take\n> responsibility for your action.\n\nSorry, what problem are you trying to solve? Does that \"checkout -f\" meant\nto nuke the local changes that are not yet at a good \"stop point\"?\n"},{"id":"176978","messageId":"CACsJy8D5FGr3R0tLYOND0kKNct4e_KgYfLUK8xL2Q4uNzWczgQ@mail.gmail.com","threadId":"28582","inReplyTo":"7vaa9f59p5.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-10-05T23:11:26Z","receivedAt":"2011-10-05T23:11:26Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Oct 6, 2011 at 9:56 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> On Thu, Oct 6, 2011 at 5:19 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> I do not necessarily think that it is a good approach to forbid the same\n>>> branch to be checked out in two different places, by the way. One reason\n>>> people would want to keep multiple workdirs is so that while they are\n>>> still working on a branch and are not yet at a good \"stop point\" to even\n>>> make a temporary commit to get interrupted, they find it sometimes\n>>> necessary to be able to build the tip of that same branch and even make a\n>>> small in-working-tree fixes (which later will be carried back to the\n>>> primary branch). The problem arises only when one of the repositories try\n>>> to update or delete the branch while it is checked out in another working\n>>> tree.\n>>\n>> I think of two options:\n>>\n>>  - detach from the already locked branch (pretty much like what we do\n>> with tags now)\n>>\n>>  - refuse normally but let \"checkout -f\" do it anyway. However the\n>> checkout lock will remain at the original worktree. If you want to\n>> update branch from the second checkout, do \"commit -f\" and take\n>> responsibility for your action.\n>\n> Sorry, what problem are you trying to solve? Does that \"checkout -f\" meant\n> to nuke the local changes that are not yet at a good \"stop point\"?\n\nI meant \"git checkout\" on the already locked branch is refused, but\n\"git checkout -f\" in that case will act just like \"git checkout\"\nignoring all locks. But I forgot that \"git checkout -f\" also discards\nworktree changes. Maybe \"git checkout --ignore-locks\" instead of \"git\ncheckout -f\".\n-- \nDuy\n"},{"id":"176980","messageId":"7vwrcj3sow.fsf@alter.siamese.dyndns.org","threadId":"28582","inReplyTo":"CACsJy8D5FGr3R0tLYOND0kKNct4e_KgYfLUK8xL2Q4uNzWczgQ@mail.gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-05T23:49:19Z","receivedAt":"2011-10-05T23:49:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n\n> On Thu, Oct 6, 2011 at 9:56 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>> I think of two options:\n>>> ...\n>> Sorry, what problem are you trying to solve? Does that \"checkout -f\" meant\n>> to nuke the local changes that are not yet at a good \"stop point\"?\n>\n> I meant \"git checkout\" on the already locked branch is refused, but\n> \"git checkout -f\" in that case will act just like \"git checkout\"\n> ignoring all locks. But I forgot that \"git checkout -f\" also discards\n> worktree changes. Maybe \"git checkout --ignore-locks\" instead of \"git\n> checkout -f\".\n\nI see what you mean, but doesn't it feel as if it is working around a\nproblem that is introduced only because of a wrong policy (i.e. \"you\ncannot check out the same branch at two places\", as opposed to \"viewing\nthem in multiple places is perfectly fine, but no touching\")?\n\nThis reminds me of how we ended up handling the \"scary warning\" around\ndetached HEAD. It is not wrong nor even dangerous to detach. It is not\nwrong nor even dangerous to make commits on detached HEAD. It is however\ndangerous to switch away from that state without saving it to a ref, and\nthat is where we give warnings.\n"},{"id":"176983","messageId":"CAG+J_DzZrFx2v09zNxKm2xyA82MyKRTq3AEus3QthtpZYhQn0A@mail.gmail.com","threadId":"28582","inReplyTo":"7vwrcj3sow.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-06T00:33:35Z","receivedAt":"2011-10-06T00:33:35Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Wed, Oct 5, 2011 at 7:49 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> This reminds me of how we ended up handling the \"scary warning\" around\n> detached HEAD. It is not wrong nor even dangerous to detach. It is not\n> wrong nor even dangerous to make commits on detached HEAD. It is however\n> dangerous to switch away from that state without saving it to a ref, and\n> that is where we give warnings.\n\nIf you have the same branch in two workdirs, then if you commit to\nthat branch in one workdir, you have to reset --hard in the other. In\nthat case, wouldn't it make more sense to just use a detached head in\nthe second workdir?\n\n  $ git checkout topic\n  fatal: branch 'topic' is currently checked out in '...'\n  $ git checkout topic^0\n  ... topic is updated elsewhere ...\n  $ git reset --hard topic\n\nEither way you need to use reset --hard if topic is updated outside of\nthe current workdir, but at least if git encourages you to detach\nfirst, you don't accidentally undo a commit.\n\nAlso, if we wait till commit time to tell the user \"sorry, topic's\nbeen updated elsewhere\", now the user is in a perilous state. They\nhave uncommitted work which they clearly want on topic. And they have\nto think about what steps are needed to get it there.\n\nSo, I really don't think this is quite analogous to detached HEAD, nor\npushing into a repo's checked out branch. In both those cases, at\nleast the user's work is already committed.\n\nBetter to prevent checking out the same branch in multiple workdirs\nwith an override for users that want risk shooting their foot off.\n\nj.\n"},{"id":"176984","messageId":"7vsjn73q6j.fsf@alter.siamese.dyndns.org","threadId":"28582","inReplyTo":"CAG+J_DzZrFx2v09zNxKm2xyA82MyKRTq3AEus3QthtpZYhQn0A@mail.gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-06T00:43:32Z","receivedAt":"2011-10-06T00:43:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jay Soffian <jaysoffian@gmail.com> writes:\n\n> On Wed, Oct 5, 2011 at 7:49 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> This reminds me of how we ended up handling the \"scary warning\" around\n>> detached HEAD. It is not wrong nor even dangerous to detach. It is not\n>> wrong nor even dangerous to make commits on detached HEAD. It is however\n>> dangerous to switch away from that state without saving it to a ref, and\n>> that is where we give warnings.\n>\n> If you have the same branch in two workdirs, then if you commit to\n> that branch in one workdir, you have to reset --hard in the other. In\n> that case, wouldn't it make more sense to just use a detached head in\n> the second workdir?\n\nNot at all. My build infrastructure determines where to install the built\nbinary based on what branch is checked out. Having a head detached at a\ncommit that is at the tip of one branch is not necessarily the same as\nhaving the branch actually checked out.\n\n> Also, if we wait till commit time to tell the user \"sorry, topic's\n> been updated elsewhere\", now the user is in a perilous state.\n\nWouldn't the \"elsewhere\" user would be warned before being able to update\nthe branch? I thought the whole point of your adding \"this branch is\nchecked out over there\" is exactly so that the \"elsewhere\" user can come\ntalk to you before that happens. These two people might be yourself, of\ncourse.\n"},{"id":"176988","messageId":"CAG+J_DxXcvF3tBPkf7ZEtiXvEK80zYJvP1rNx-PagM8TV-1KSA@mail.gmail.com","threadId":"28582","inReplyTo":"7vsjn73q6j.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-06T00:57:35Z","receivedAt":"2011-10-06T00:57:35Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Wed, Oct 5, 2011 at 8:43 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Not at all. My build infrastructure determines where to install the built\n> binary based on what branch is checked out. Having a head detached at a\n> commit that is at the tip of one branch is not necessarily the same as\n> having the branch actually checked out.\n\nThat's fair, but I'm willing to wager that's a minority use-case. Not\nthat it shouldn't be possible, but perhaps it should require telling\ngit that's really what you want to do with checkout --force.\n\n>> Also, if we wait till commit time to tell the user \"sorry, topic's\n>> been updated elsewhere\", now the user is in a perilous state.\n>\n> Wouldn't the \"elsewhere\" user would be warned before being able to update\n> the branch? I thought the whole point of your adding \"this branch is\n> checked out over there\" is exactly so that the \"elsewhere\" user can come\n> talk to you before that happens. These two people might be yourself, of\n> course.\n\nSo you're envisioning this?\n\n  $ git commit foo.c\n  Warning, master is also checked out in workdir2\n\nHow does that help the user? Now they have to go to workdir2 and reset\n--hard. Is that really something we want to encourage?\n\nAnd what if they do this:\n\n  $ cd workdir1\n  $ edit foo.c\n  ... time passes...\n  $ cd workdir2\n  $ edit foo.c\n  $ git commit foo.c\n  Warning, master is also checked out in workdir1\n\nj.\n"},{"id":"176990","messageId":"7v62k253ad.fsf@alter.siamese.dyndns.org","threadId":"28582","inReplyTo":"CAG+J_DxXcvF3tBPkf7ZEtiXvEK80zYJvP1rNx-PagM8TV-1KSA@mail.gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-06T01:15:06Z","receivedAt":"2011-10-06T01:15:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jay Soffian <jaysoffian@gmail.com> writes:\n\n> So you're envisioning this?\n>\n>   $ git commit foo.c\n>   Warning, master is also checked out in workdir2\n\nNo. I would rather think it needs to be forced.\n"},{"id":"176991","messageId":"CAG+J_Dz++SG28a=DhZ+Doz1np21jMavYpc0hKfe1rgq-dHZLPA@mail.gmail.com","threadId":"28582","inReplyTo":"7v62k253ad.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-06T01:38:42Z","receivedAt":"2011-10-06T01:38:42Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Wed, Oct 5, 2011 at 9:15 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jay Soffian <jaysoffian@gmail.com> writes:\n>\n>> So you're envisioning this?\n>>\n>>   $ git commit foo.c\n>>   Warning, master is also checked out in workdir2\n>\n> No. I would rather think it needs to be forced.\n\nNow they do what? Either commit --force or create a new branch?\nWouldn't it have been better to create the new branch before they\nstarted editing?\n\nHere's what I'm trying to avoid:\n\n  $ cd workdir2\n  $ git checkout master\n  $ edit foo.c\n  $ git commit foo.c\n  By default, committing to a branch that is checked out in more than\n  one location is denied, because it will make the index and work tree\n  inconsistent in the other locations and will require 'git reset --hard'\n  to match the work tree to HEAD in each of those other locations.\n\n  Either switch to a new branch first with 'git checkout -b <new_branch>'\n  or use 'git commit --force' to override this message.\n\nUser: \"crap, I wanted that on master\". Now they do what. Something like:\n\n  $ git checkout -b foo\n  $ git commit foo.c\n  $ cd workdir1\n  $ git cherry-pick foo\n  $ git branch -d foo\n\nOr maybe they use stash instead. In either case, I just think that's a\nterrible user experience compared to:\n\n $ cd workdir2\n $ git checkout master\n error: master already checked out in workdir1\n $ cd workdir1\n $ edit foo.c\n $ git commit foo.c\n\nI guess it depends what you mostly use your workdirs for. For me, it's\nto have different branches checked out, not to have the same branch\nchecked out in multiple locations. I want git to help me up front, not\nwhen I go to commit.\n\nj.\n"},{"id":"176992","messageId":"7v1uuq51c3.fsf@alter.siamese.dyndns.org","threadId":"28582","inReplyTo":"CAG+J_Dz++SG28a=DhZ+Doz1np21jMavYpc0hKfe1rgq-dHZLPA@mail.gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2011-10-06T01:57:16Z","receivedAt":"2011-10-06T01:57:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jay Soffian <jaysoffian@gmail.com> writes:\n\n> On Wed, Oct 5, 2011 at 9:15 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Jay Soffian <jaysoffian@gmail.com> writes:\n>>\n>>> So you're envisioning this?\n>>>\n>>>   $ git commit foo.c\n>>>   Warning, master is also checked out in workdir2\n>>\n>> No. I would rather think it needs to be forced.\n>\n> Now they do what? Either commit --force or create a new branch?\n> Wouldn't it have been better to create the new branch before they\n> started editing?\n\nIf they are going to commit, and if they knew that they are going to\ncommit, yes.\n\nBut why do you want to forbid people from just checking things out if they\nare not interested in committing? That is where I think you are going\nbackwards.\n\n> I guess it depends what you mostly use your workdirs for. For me, it's\n> to have different branches checked out, not to have the same branch\n> checked out in multiple locations.\n\nThen you wouldn't have any problem if commit refused to make commit on the\nbranch that is checked out elsewhere, no?\n\nI am not saying we should never have an option to _warn_ checking out the\nsame branch in multiple places. I am saying it is wrong to forbid doing so\nby default.\n"},{"id":"176994","messageId":"CACsJy8DZE5jSnOuraaVaW1+nA-hUiTXsNLJYEG+32qEJ1irGiQ@mail.gmail.com","threadId":"28582","inReplyTo":"7vwrcj3sow.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Nguyen Thai Ngoc Duy","fromEmail":"pclouds@gmail.com","sentAt":"2011-10-06T02:06:25Z","receivedAt":"2011-10-06T02:06:25Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Thu, Oct 6, 2011 at 10:49 AM, Junio C Hamano <gitster@pobox.com> wrote:\n> Nguyen Thai Ngoc Duy <pclouds@gmail.com> writes:\n>\n>> On Thu, Oct 6, 2011 at 9:56 AM, Junio C Hamano <gitster@pobox.com> wrote:\n>>>> I think of two options:\n>>>> ...\n>>> Sorry, what problem are you trying to solve? Does that \"checkout -f\" meant\n>>> to nuke the local changes that are not yet at a good \"stop point\"?\n>>\n>> I meant \"git checkout\" on the already locked branch is refused, but\n>> \"git checkout -f\" in that case will act just like \"git checkout\"\n>> ignoring all locks. But I forgot that \"git checkout -f\" also discards\n>> worktree changes. Maybe \"git checkout --ignore-locks\" instead of \"git\n>> checkout -f\".\n>\n> I see what you mean, but doesn't it feel as if it is working around a\n> problem that is introduced only because of a wrong policy (i.e. \"you\n> cannot check out the same branch at two places\", as opposed to \"viewing\n> them in multiple places is perfectly fine, but no touching\")?\n\nWell, we could do change the default so \"git checkout\" == \"git\ncheckout --ignore-locks\".\n\n\"git commit --ignore-locks\" would commit without checking locks. \"git\ncommit\" could either:\n\n - reject because it does not hold the lock (to hostile?)\n\n - detach automatically then commit\n\nThe latter has a benefit that we can now checkout tags without\ndetaching from the beginning. \"git branch\" would show tag name until\nyou commit.\n-- \nDuy\n"},{"id":"177002","messageId":"CAG+J_DwEx9y-5B+ZppW1jURCYE2f-rkniYnRFjEtd4+spPurQA@mail.gmail.com","threadId":"28582","inReplyTo":"7v1uuq51c3.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jay Soffian","fromEmail":"jaysoffian@gmail.com","sentAt":"2011-10-06T04:02:03Z","receivedAt":"2011-10-06T04:02:03Z","isPatch":true,"sender":{"key":"jaysoffian@gmail.com","avatar":"https://avatars.githubusercontent.com/u/155970?v=4"},"body":"On Wed, Oct 5, 2011 at 9:57 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Jay Soffian <jaysoffian@gmail.com> writes:\n>> Now they do what? Either commit --force or create a new branch?\n>> Wouldn't it have been better to create the new branch before they\n>> started editing?\n>\n> If they are going to commit, and if they knew that they are going to\n> commit, yes.\n\nCommitting is obviously the common case for a checked-out branch.\n\n> But why do you want to forbid people from just checking things out if they\n> are not interested in committing? That is where I think you are going\n> backwards.\n\nBecause if they do decide to commit, it's now harder for them to do so.\n\nIt would be great if git could intervene after the checkout, but\nbefore they edit any files, so that they don't have uncommitted work.\nObviously that's not possible, so git should prevent them from getting\nto that point.\n\nLet's consider the various situations:\n\n1. master is checked out w/edits in workdir1, user wants to work on\ntopic in workdir2.\n\nThere's nothing to warn about in workdir2 neither at checkout nor commit time.\n\n2. master is checked out w/edits in workdir1, user wants examine\nunedited master in workdir2\n\nAt checkout time in workdir2:\n\nMy preference: checkout advices user to use --detach or --force.\nYour preference: checkout is silent.\n\nNow user decides they want to commit to master in workdir2 (which is\ninsane, they've got uncommitted changes to it in workdir1). What\nhappens?\n\nIn my scenario, the commit happens on a detached HEAD. When they\neventually switch back to a branch, git tells them how to move their\ncommit to a branch.\n\nIn your scenario, commit complains. User now has to --force, stash, or\ncreate a new branch.\n\nIt's just seems insane to me putting in obstacles to the user\ncommitting their work. That's where I think you are going backwards.\n\nYou have a use case where using a detached HEAD doesn't work because\nyou've scripted around the same branch multiply checked out. I think\nthat's probably an exceedingly rare use case, and justifies \"checkout\n--force\".\n\n>> I guess it depends what you mostly use your workdirs for. For me, it's\n>> to have different branches checked out, not to have the same branch\n>> checked out in multiple locations.\n>\n> Then you wouldn't have any problem if commit refused to make commit on the\n> branch that is checked out elsewhere, no?\n\nYes, I would, because by that point, I've already made the mistake of\nchecking out the same branch twice. I want git to prevent me from\ndoing that by accident. Because I don't want to ever be in the\nsituation which comes next, which is that I've got uncommitted work\nfor the same branch in two places!\n\n> I am not saying we should never have an option to _warn_ checking out the\n> same branch in multiple places. I am saying it is wrong to forbid doing so\n> by default.\n\nI am not saying we should never have an option to allow checking out\nthe same branch in multiple places. I am saying it is wrong to allow\ndoing so by default.\n\nj.\n"},{"id":"177014","messageId":"20111006112530.GB27897@server.brlink.eu","threadId":"28582","inReplyTo":"7vzkhf713u.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Bernhard R. Link","fromEmail":"brl+git@mail.brlink.eu","sentAt":"2011-10-06T11:25:30Z","receivedAt":"2011-10-06T11:25:30Z","isPatch":true,"sender":{"key":"brl+git@mail.brlink.eu","avatar":null},"body":"* Junio C Hamano <gitster@pobox.com> [111005 20:19]:\n> I do not necessarily think that it is a good approach to forbid the same\n> branch to be checked out in two different places, by the way. [...]\n> [...] The problem arises only when one of the repositories try\n> to update or delete the branch while it is checked out in another working\n> tree.\n\nI think this is mostly the same problem that also make pushing to a\nchecked out branch a problem.\n\nIsn't the real problem that a checked out branch / a branch having a\nworkdir only has information what branch it belongs to?\n\nWouldn't both problems (multiple workdirs of the same branch, pushing\nto a checked out branch) solved if each working directory (including\nthe default one in a non-bare repository) also stored the commit id\nlast checked out? (And then giving a warning, error or automatically\ncreating a detached head setting whenever the branch it followed is\nmoved behind it's back?)\n\n\tBernhard R. Link\n"},{"id":"177033","messageId":"20111006144257.GB21558@sigill.intra.peff.net","threadId":"28582","inReplyTo":"7vzkhf713u.fsf@alter.siamese.dyndns.org","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2011-10-06T14:42:57Z","receivedAt":"2011-10-06T14:42:57Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Oct 05, 2011 at 11:19:17AM -0700, Junio C Hamano wrote:\n\n> Jay Soffian <jaysoffian@gmail.com> writes:\n> \n> > Git has survived w/o needing to lock branches till now.\n> \n> Careful. Git has survived without your patch series till now, as people\n> learned to be careful when they use separate workdirs and avoid certain\n> things, to the point that they are not necessarily aware that they are\n> avoiding them (one good practice is to keep the HEADs of non-primary\n> workdirs detached).\n> \n> Does that mean what your patch aims to do is unnecessary? I think not.\n\nIt seems to me that things like receive.denyCurrentBranch and\nreceive.denyDeleteCurrent are just special hand-rolled versions of the\nsame concept.\n\nCould they be implemented using this kind of branch locking? Moreover, I\nthink they would need to be to cope with new-workdir, as the definition\nof \"current\" stops being \"referenced by HEAD\", but becomes much larger.\n\n-Peff\n"},{"id":"177253","messageId":"loom.20111009T000812-294@post.gmane.org","threadId":"28582","inReplyTo":"1317786204-57335-1-git-send-email-jaysoffian@gmail.com","subject":"Re: [RFC/PATCH] Add multiple workdir support to branch/checkout","fromName":"Julián Landerreche","fromEmail":"maniqui@gmail.com","sentAt":"2011-10-08T22:55:22Z","receivedAt":"2011-10-08T22:55:22Z","isPatch":true,"sender":{"key":"maniqui@gmail.com","avatar":null},"body":"Jay wrote:\n\n> I guess it depends what you mostly use your workdirs for. For me, it's \n> to have different branches checked out, not to have the same branch\n> checked out in multiple locations. \n\nI find those both use cases for workdirs to fit perfectly in my usual workflow \n(web development).\n\n- Different branches checked out\nI've a cloned repo of a CMS and I use git-new-workdir to checkout different \nbranches and tags, so to have available a few workdirs of recent versions, which \nI \"attach\" by symlinks to my web development projects.\n\n- Same branch checked out in multiple locations\nThis use case just came up recently, when I find out that I prefer to have two \nwebsites \"attached\" (via symlink) to two different workdirs of the same branch.\nI could have \"attached\" both websites to the same workdir, but my idea of having \nthe websites \"attached\" to different workdirs was to be able to do some \ndevelopment (i.e: to commit stuff) on one workdir, while keeping the other one \n\"fixed\" at some particular commit. \n\nJuno wrote:\n\n> Careful. Git has survived without your patch series till now, as people\n> learned to be careful when they use separate workdirs and avoid certain\n> things, to the point that they are not necessarily aware that they are\n> avoiding them (one good practice is to keep the HEADs of non-primary\n> workdirs detached).\n\nJay wrote:\n\n> Also, while I might recommend new-workdir to my coworkers with the\n> advice \"don't checkout the same branch in multiple workdirs\", never in\n> a million years would I say \"use new-workdir, but make sure to only\n> use a detached HEAD in the workdirs.\" The latter would make their\n> actual HEADs explode. \n\nAfter reading this, I noticed that using git-new-workdir with detached HEAD in \neach workdir could fit my workflow very well. In some cases (the ones mentioned \nabove), I find that I may not need to have a workdir for a branch (where I won't \ndo work, so won't be committing there), but rather to have that workdir \"fixed\" \nat a particular commit. \nThat being said, I also see that I would find useful to be able to \nupdate/advance this workdir (in a detached HEAD state, that is, \"fixed\" at an \nspecific commit) to a newer commit or to a particular branch.\n\nBottom line: making git-new-workdir a more reliable & friendly tool that could \nfit in the workflows of both advanced and non-advanced users.\n\n-----\nQuick note about me: I am an \"advanced n00b\" on git usage (using it since one \nyear ago), and a general non-advanced user (of git and git-new-workdir). In \nother words, a git user that could easily shoot itself in the foot.\n\nArrived here while looking for some info about git-new-workdir, and if it was a \nBad Idea to have different workdirs of the same branch (ie. checkout the same \nbranch on different folders), as the idea of recklessly committing on different \nworkdirs for the same branch sounded like a recipe for disasters to me.\n\nI find it git-new-workdir a really useful tool in my workflow, and prefer it \nover having many clones of the same repo, which will imply having to do \nconfiguration for remotes and push/pull operations, which are also mind-boggling \ntasks for non-advanced users.\n-----\n\nThanks for reading.\n"}]}