{"thread":{"id":"14981","subject":"[TopGit PATCH] tg-create.sh: Introduce --add option to add a dependency.","startedAt":"2008-08-13T14:25:14Z","lastAt":"2008-08-18T09:18:23Z","messageCount":5,"participants":["Jan Nieuwenhuizen","Jonathan Nieder"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"87051","messageId":"1218637514.7561.30.camel@heerbeest","threadId":"14981","inReplyTo":null,"subject":"[TopGit PATCH] tg-create.sh: Introduce --add option to add a dependency.","fromName":"Jan Nieuwenhuizen","fromEmail":"janneke-list@xs4all.nl","sentAt":"2008-08-13T14:25:14Z","receivedAt":"2008-08-13T14:25:14Z","isPatch":true,"sender":{"key":"janneke-list@xs4all.nl","avatar":null},"body":"This implements \n\n    tg create --add DEP\n\nto add dependency DEP to an already existing topgit branch.\n\nThe bad thing is that this does not play well with tg undepend;\nit won't work to re-add a previously removed dependency.  This\n--add is implemented as a merge, and all merge commits are\nalready present; it is only that lateron they are reverted.\n\nAny ideas on how to fix that?\n\nSigned-off-by: Jan Nieuwenhuizen <janneke@gnu.org>\n---\n README       |    6 +++++-\n tg-create.sh |   44 ++++++++++++++++++++++++++++++++++++--------\n 2 files changed, 41 insertions(+), 9 deletions(-)\n\ndiff --git a/README b/README\nindex 096b9ec..a9957f2 100644\n--- a/README\n+++ b/README\n@@ -215,6 +215,9 @@ tg create\n \tit will detect that you are on a topic branch base ref and\n \tresume the topic branch creation operation.\n \n+\tWith the --add option, all given arguments are added as\n+\tdependencies to current topic branch.\n+\n tg delete\n ~~~~~~~~~\n \tRemove a TopGit-controlled topic branch of given name\n@@ -333,7 +336,8 @@ tg export\n tg undepend\n ~~~~~~~~~~~\n \tUpdate the current topic branch by removing the given\n-\tbranch (required argument) from the list of dependencies.\n+\tbranch (required argument) from the list of dependencies\n+\tand reverting all its commits.\n \n tg update\n ~~~~~~~~~\ndiff --git a/tg-create.sh b/tg-create.sh\nindex 939af33..c5bc5fb 100644\n--- a/tg-create.sh\n+++ b/tg-create.sh\n@@ -3,9 +3,11 @@\n # (c) Petr Baudis <pasky@suse.cz>  2008\n # GPLv2\n \n+add= # Set to 1 when adding dependencies to existing topgit branch\n deps= # List of dependent branches\n restarted= # Set to 1 if we are picking up in the middle of base setup\n merge= # List of branches to be merged; subset of $deps\n+merged= # List branches actually merged\n name=\n \n \n@@ -14,8 +16,12 @@ name=\n while [ -n \"$1\" ]; do\n \targ=\"$1\"; shift\n \tcase \"$arg\" in\n+\t--add)\n+\t\tadd=1\n+\t\tname=$(git symbolic-ref HEAD | cut -b 12-)\n+\t\t;;\n \t-*)\n-\t\techo \"Usage: tg create NAME [DEPS...]\" >&2\n+\t\techo \"Usage: tg create --add|NAME [DEPENDENCY]...\" >&2\n \t\texit 1;;\n \t*)\n \t\tif [ -z \"$name\" ]; then\n@@ -50,12 +56,22 @@ fi\n \n [ -n \"$merge\" -o -n \"$restarted\" ] || merge=\"$deps \"\n \n+if [ -z \"$add\" ]; then\n+\t! git rev-parse --verify \"$name\" >/dev/null 2>&1 \\\n+\t\t|| die \"branch '$name' already exists\"\n+else\n+\tdupes=$(grep -E \"^${merge// /|}/\\$\" .topdeps | tr '\\n' ' ')\n+\t[ -z \"$dupes\" ] || die \"already depend on: $dupes\"\n+\tdeps=$(echo \"$merge\" | cat .topdeps - | tr '\\n' ' ' | sed -e 's/ \\+$//')\n+\tmerged=\"$merge\"\n+\tmerge=\"$name $merge\"\n+fi\n+\n+\n for d in $deps; do\n \tgit rev-parse --verify \"$d\" >/dev/null 2>&1 ||\n \t\tdie \"unknown branch dependency '$d'\"\n done\n-! git rev-parse --verify \"$name\" >/dev/null 2>&1 ||\n-\tdie \"branch '$name' already exists\"\n \n # Clean up any stale stuff\n rm -f \"$git_dir/top-name\" \"$git_dir/top-deps\" \"$git_dir/top-merge\"\n@@ -96,7 +112,13 @@ done\n \n ## Set up the topic branch\n \n-git update-ref \"refs/top-bases/$name\" \"HEAD\" \"\"\n+if [ -z \"$add\" ]; then\n+\tgit update-ref \"refs/top-bases/$name\" \"HEAD\" \"\"\n+else\n+\t#[ -n \"$add\" ] && git -D \"$name\"\n+\tgit branch -D save/\"$name\" || :\n+\tgit branch -m \"$name\" save/$name\n+fi\n git checkout -b \"$name\"\n \n echo \"$deps\" | sed 's/ /\\n/g' >\"$root_dir/.topdeps\"\n@@ -104,7 +126,7 @@ git add \"$root_dir/.topdeps\"\n \n author=\"$(git var GIT_AUTHOR_IDENT)\"\n author_addr=\"${author%> *}>\"\n-{\n+[ -z \"$add\" ] && {\n \techo \"From: $author_addr\"\n \t! header=\"$(git config topgit.to)\" || echo \"To: $header\"\n \t! header=\"$(git config topgit.cc)\" || echo \"Cc: $header\"\n@@ -120,7 +142,13 @@ EOT\n } >\"$root_dir/.topmsg\"\n git add \"$root_dir/.topmsg\"\n \n+if [ -z \"$add\" ]; then\n+\tinfo \"Topic branch $name set up. Please fill .topmsg now and make initial commit.\"\n+\tinfo \"To abort: git rm -f .top* && git checkout ${deps%% *} && tg delete $name\"\n+else\n+\tgit commit -am \"Add dependency: $merged\"\n+fi\n \n-\n-info \"Topic branch $name set up. Please fill .topmsg now and make initial commit.\"\n-info \"To abort: git rm -f .top* && git checkout ${deps%% *} && tg delete $name\"\n+# Local Variables:\n+# sh-basic-offset:8\n+# End:\n-- \n1.6.0.rc0.44.g67270\n\n\n-- \nJan Nieuwenhuizen <janneke@gnu.org> | GNU LilyPond - The music typesetter\nhttp://www.xs4all.nl/~jantien       | http://www.lilypond.org\n"},{"id":"87070","messageId":"Pine.GSO.4.62.0808131100280.1278@harper.uchicago.edu","threadId":"14981","inReplyTo":"1218637514.7561.30.camel@heerbeest","subject":"Re: [TopGit PATCH] tg-create.sh: Introduce --add option to add a dependency.","fromName":"Jonathan Nieder","fromEmail":"jrnieder@uchicago.edu","sentAt":"2008-08-13T16:20:47Z","receivedAt":"2008-08-13T16:20:47Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nJan Nieuwenhuizen wrote:\n\n> The bad thing is that this does not play well with tg undepend;\n> it won't work to re-add a previously removed dependency.  This\n> --add is implemented as a merge, and all merge commits are\n> already present; it is only that lateron they are reverted.\n> \n> Any ideas on how to fix that?\n\nInteresting - I had imagined changing dependencies working in an\nentirely different way.\n\nLet's say your history is\n\n    B -- (some mess) -- P\n\nwhere P is your current branch head, and B is the current top base, a\nmerge of all your current dependencies.  Now you change your\ndependencies drastically, and you end up with a new top base B'.  Then\nideally you want your new branch head P' to have the same tree as if you\nhad run \"git rebase --onto B' B P\", but of course you want to preserve\nthe history, so you could\n\n\t$ git checkout -b P' P\n\t$ git rebase --onto B' B\n\t$ git checkout P\n\t$ git merge --no-ff --no-commit B'   (*)\n\t$ git read-tree -u P'\n\t$ git commit\n\t$ git branch -D P'\n\nor something like that.  The line marked with a (*) doesn't work as it\nshould in current git as far as I remember, but it would be a simple\nchange.  The point is to achieve the result\n\n    B -- (some mess) -- old P -- P\n                                /\n                              B'\n\nwith the diff from B to old P being approximately the same as the diff\nfrom B' to P, even if we just dropped some dependencies.\n\nThe main problem I see with this story is that if B' is just B with some\nnew changes added this is overly complicated.  In other words, in the\nsimple case of moving from (say) a patch based on master to a patch\nbased on next, one would probably prefer\n\n    B -- (some mess) -- old P -- P\n     \\                         /\n      ---------------------- B'\n\nand similarly for moving from depending on master+(one additional patch)\nto next+(that same patch).\n\nHope this helps, and sorry I don't have something more constructive to\nsay.  Thanks for starting this going.\n\nJonathan\n"},{"id":"87296","messageId":"1218787834.7585.13.camel@heerbeest","threadId":"14981","inReplyTo":"Pine.GSO.4.62.0808131100280.1278@harper.uchicago.edu","subject":"Re: [TopGit PATCH] tg-create.sh: Introduce --add option to add a dependency.","fromName":"Jan Nieuwenhuizen","fromEmail":"janneke-list@xs4all.nl","sentAt":"2008-08-15T08:10:34Z","receivedAt":"2008-08-15T08:10:34Z","isPatch":true,"sender":{"key":"janneke-list@xs4all.nl","avatar":null},"body":"On wo, 2008-08-13 at 11:20 -0500, Jonathan Nieder wrote:\n\nHi,\n\n> Interesting - I had imagined changing dependencies working in an\n> entirely different way.\n\nThanks!  This is quite interesting.  A few questions\n\n> \n> \t$ git checkout -b P' P\n> \t$ git rebase --onto B' B\n\n.. is using rebase a robust solution?  We should provide a way to\nrecover after user intervention here?\n\n> \t$ git checkout P\n> \t$ git merge --no-ff --no-commit B'   (*)\n\nDo you remember in what area the problem is here, that would make it a \nlot easier for me to look.\n\n> \t$ git read-tree -u P'\n\nOuch, I'm feeling so git-unitiated here; what is read-tree doing \ndifferently from merge?  Isn't here a -m missing?\n\n> The main problem I see with this story is that if B' is just B with some\n> new changes added this is overly complicated.\n\nYes, that's my main gripe.  One of the use cases I'm looking at is\nour ooo-build master branch; which includes ~300 topic branches.\n\nRemoving or [re-]adding one dependency using this rebase-by-merging \napproch would take ~7 minutes on my machine.\n\nI'm now also looking at a .topundeps file, to support\nthe re-adding of a depenency using the cherry-pick approach...\n\nGreetings,\nJanneke. \n\n-- \nJan Nieuwenhuizen <janneke@gnu.org> | GNU LilyPond - The music typesetter\nhttp://www.xs4all.nl/~jantien       | http://www.lilypond.org\n"},{"id":"87318","messageId":"Pine.GSO.4.62.0808150913180.9955@harper.uchicago.edu","threadId":"14981","inReplyTo":"1218787834.7585.13.camel@heerbeest","subject":"Re: [TopGit PATCH] tg-create.sh: Introduce --add option to add a dependency.","fromName":"Jonathan Nieder","fromEmail":"jrnieder@uchicago.edu","sentAt":"2008-08-15T15:25:50Z","receivedAt":"2008-08-15T15:25:50Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nI think my last message missed your real point.  Let P be the old topic\nbranch tip, B the old base, P' the new tip, and B' the new base.  There\nare four questions to answer:\n\n1) How should the tree of B' be determined?\n2) What should the parents of B' be?\n3) How should the tree of P' be determined from P, B, and B'?\n4) What should the parents of P' be?\n\nI gave my answers to 2, 3, and 4, but I think the problem you ran into\nis already there in question 1.  It comes from the basic model of how\nTopGit works, not some design decision you made, I think.  Suppose I\n\n\tMake a topic branch t/foo depending on master.\n\tChange the dependency of t/foo to the older version maint.\n\tMake a new topic branch t/bar depending on t/foo and master.\n\nBecause in TopGit we do not ever rewrite history on the t/foo branch,\nmaster is a parent of t/foo even after the dependencies change.  So\nmerging t/foo and master gives just t/foo, and the base for the new\ntopic branch does not incorporate the changes from maint to master at\nall.\n\nBut note that this has nothing to do with how the second step takes\nplace - the problem occurs as long as we are not throwing away history\nand we are using merges to calculate the topic branch bases.\n\nWe would really like in the third step to calculate a three-way merge of\nt/foo with master, with maint as merge base.  The merge-recursive magic\n(making use of the history between maint and t/foo) is just a recipe for\ntrouble here, and we know better than the automatically chosen merge\nbase (master).\n\nSo:\n\n\tfoo_deps=$(git show t/foo:.topdeps)\n\tmerge_bases=$(git merge-base --all master $foo_deps)\n\tgit merge-resolve $merge_bases -- master t/foo\n\nshould give the right effect in this case (even if t/foo has multiple\ndependencies).  This example fails to take into account the case where\nmaster and t/foo are completely independent, but that is easily fixed.\nBut if t/foo depends on other topic branches in turn, it is not so easy\nto fix - I am not sure what to do then.  I am tempted to suggest some\ninsanity like making the dependency graph into history with grafts...\n\nSo all this trouble is there when we try to come up with the topic base\nfor a new topic, as long as it is possible /in some way/ to weaken\ndependencies.\n\nOk, onto your questions about 2, 3, and 4:\n\n> > \t$ git checkout -b P' P\n> > \t$ git rebase --onto B' B\n> \n> .. is using rebase a robust solution?  We should provide a way to\n> recover after user intervention here?\n\nIt is the temporary P' branch that is being rebased, so we can recover\nby checking out P and throwing away the P' branch.  I suggested rebase\nbecause there is UI for skipping a change, etc. but the more I think\nabout it, the more I think it would make sense to just\n\n\t...\n\t$ git checkout -b P' B'\n\t$ {\n\t>\techo Subject: temporary commit &&\n\t>\techo --- &&\n\t>\tgit diff -M B P\n\t> } | git am -3\n\t...\n\nor, if we make B a parent of B',\n\n\t$ git checkout P\n\t$ git merge B'\n\nwhich might be preferrable.\n\n> > \t$ git checkout P\n> > \t$ git merge --no-ff --no-commit B'   (*)\n> \n> Do you remember in what area the problem is here, that would make it a \n> lot easier for me to look.\n\nI tried this out, and it seems here I was worrying needlessly.  I was\nprobably thinking of the following, which might or might not be\nintentional:\n\n\t$ mkdir newrepo && cd newrepo\n\t$ git init\n\t$ git remote add upstream ../oldrepo\n\t$ git fetch upstream\n\t$ git merge --no-ff --no-commit upstream/master\n\nWe asked for no commit, but because we are on a branch yet to be born,\nthe merge makes a commit anyway.\n\n> > \t$ git read-tree -u P'\n> \n> Ouch, I'm feeling so git-unitiated here; what is read-tree doing \n> differently from merge?  Isn't here a -m missing?\n\nWhy?  We want to just blindly take the tree from P' and using it.  The\npoint is to make setting the contents of the new branch tip and its\nparentage separate decisions.\n\n> > The main problem I see with this story is that if B' is just B with some\n> > new changes added this is overly complicated.\n> \n> Yes, that's my main gripe.  One of the use cases I'm looking at is\n> our ooo-build master branch; which includes ~300 topic branches.\n> \n> Removing or [re-]adding one dependency using this rebase-by-merging \n> approch would take ~7 minutes on my machine.\n\nCan you be more precise here?  What user action causes topgit to do\nso much work (adding one dependency to what topic)?  What other\napproach avoids all this work?\n \n> I'm now also looking at a .topundeps file, to support\n> the re-adding of a depenency using the cherry-pick approach...\n\nDoes it address the situation I mention at the top of this file?\n\nThanks for clarifying, and sorry I missed your point before.  I'll take\na look at your patch now.\n\nRegards,\nJonathan\n"},{"id":"87532","messageId":"1219051103.8816.29.camel@heerbeest","threadId":"14981","inReplyTo":"Pine.GSO.4.62.0808150913180.9955@harper.uchicago.edu","subject":"Re: [TopGit PATCH] tg-create.sh: Introduce --add option to add a dependency.","fromName":"Jan Nieuwenhuizen","fromEmail":"janneke-list@xs4all.nl","sentAt":"2008-08-18T09:18:23Z","receivedAt":"2008-08-18T09:18:23Z","isPatch":true,"sender":{"key":"janneke-list@xs4all.nl","avatar":null},"body":"On vr, 2008-08-15 at 10:25 -0500, Jonathan Nieder wrote:\n\nHi,\n\n> 1) How should the tree of B' be determined?\n> 2) What should the parents of B' be?\n> 3) How should the tree of P' be determined from P, B, and B'?\n> 4) What should the parents of P' be?\n\n> So all this trouble is there when we try to come up with the topic \n> base\n> for a new topic, as long as it is possible /in some way/ to weaken\n> dependencies.\n\nWhich I have just been avoiding...  Thanks, that clarifies things for\nme.\n\n> or, if we make B a parent of B',\n> \n> \t$ git checkout P\n> \t$ git merge B'\n> \n> which might be preferrable.\n\n> I tried this out, and it seems here I was worrying needlessly.\n\nOk, good to know.\n\n> > Ouch, I'm feeling so git-unitiated here; what is read-tree doing \n> > differently from merge?  Isn't here a -m missing?\n> \n> Why?  We want to just blindly take the tree from P' and using it.  The\n> point is to make setting the contents of the new branch tip and its\n> parentage separate decisions.\n\nHmm, for one, \"git read-tree -u SHA\" does not work for me.\n\n> > Removing or [re-]adding one dependency using this rebase-by-merging \n> > approch would take ~7 minutes on my machine.\n> \n> Can you be more precise here?  What user action causes topgit to do\n> so much work (adding one dependency to what topic)?  What other\n> approach avoids all this work?\n\nGood question, I was thinking a bit careless here.  Creating the master\ntopgit branch, which depends on ~300 single-topic topgit branches, like\nso\n\n    tg create t/master $(git branch | grep -vE '/|patched|pristine')\n\ntakes ~7 minutes; which does not really surprise me.\n\nI realise now that if we make a strict *adding* of dependencies a\nspecial case, we can just checkout our old B, and add (merge) the\nadditional dependencies on top of that.\n\nHowever, when *removing* a dependency, I see no easy way to do that cheaply.\n\nGreetings,\nJanneke\n\n-- \nJan Nieuwenhuizen <janneke@gnu.org> | GNU LilyPond - The music typesetter\nhttp://www.xs4all.nl/~jantien       | http://www.lilypond.org\n"}]}