{"thread":{"id":"60543","subject":"Orphan branch not well-defined?","startedAt":"2023-11-22T00:35:58Z","lastAt":"2023-11-24T02:31:42Z","messageCount":7,"participants":["Craig H Maynard","Junio C Hamano","Chris Torek","Eric Sunshine"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"485042","messageId":"FE2AD666-88DE-4F70-8D6D-3A426689EB41@me.com","threadId":"60543","inReplyTo":null,"subject":"Orphan branch not well-defined?","fromName":"Craig H Maynard","fromEmail":"chmaynard@me.com","sentAt":"2023-11-22T00:28:36Z","receivedAt":"2023-11-22T00:35:58Z","isPatch":false,"sender":{"key":"chmaynard@me.com","avatar":"https://gravatar.com/avatar/28c48bd37ac090e8adc0ea797e69c0d0965121d535644cdb6eb6ec15dfa7df6d?d=mp&s=160"},"body":"Team,\n\nRecently I tried creating an orphan branch in an existing repo using git-checkout and git-switch. \n\nBoth commands have an --orphan option.\n\nThe results were different:\n\ngit checkout --orphan <newbranch> retained the entire working tree, including subdirectories and their contents.\n\ngit switch --orphan <newbranch> retained subdirectories but NOT their content.\n\nLeaving aside the question of whether or not this is a bug, there doesn't appear to be any formal definition of the term \"orphan branch\" in the git documentation. Am I missing something?\n\nThanks,\nCraig\n\n\n"},{"id":"485043","messageId":"xmqqil5uegrg.fsf@gitster.g","threadId":"60543","inReplyTo":"FE2AD666-88DE-4F70-8D6D-3A426689EB41@me.com","subject":"Re: Orphan branch not well-defined?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-11-22T01:08:51Z","receivedAt":"2023-11-22T01:09:02Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Craig H Maynard <chmaynard@me.com> writes:\n\n> Recently I tried creating an orphan branch in an existing repo using git-checkout and git-switch. \n>\n> Both commands have an --orphan option.\n>\n> The results were different:\n\nThis is one of the very much deliberate differences between \"switch\"\nand \"checkout\" (there are others).\n\nThe \"switch\" command was introduced as an experiment to figure out a\nbetter UI choices for one half of \"checkout\", which deals with\nchecking out a branch to a working tree (the other half being\n\"restore\", which is about checking out files out of a tree-ish).\nThe initial round of \"switch\" proposed to go with the identical\nsemantics as \"checkout\" for the \"--orphan\" option [*1*], but during\nreview discussion, a concensus was reached that a better behaviour\nfor creating an entirely new history may be to start from void\n[*2*], and that is what is in the experimental command you see.\n\n\n[Reference]\n\n*1* https://lore.kernel.org/git/20190130094831.10420-9-pclouds@gmail.com/\n\n*2* https://lore.kernel.org/git/CABPp-BF3_p3+fmQcWYEu2z3J4FfPmDmiMyFiBRXyz8TxKLL7jA@mail.gmail.com/\n\n"},{"id":"485044","messageId":"CAPx1Gvf_meaEBq9XfS9aPg0yLja-2sAW5SUg0jx6f1jNyfmWHw@mail.gmail.com","threadId":"60543","inReplyTo":"FE2AD666-88DE-4F70-8D6D-3A426689EB41@me.com","subject":"Re: Orphan branch not well-defined?","fromName":"Chris Torek","fromEmail":"chris.torek@gmail.com","sentAt":"2023-11-22T01:42:42Z","receivedAt":"2023-11-22T01:42:55Z","isPatch":false,"sender":{"key":"chris.torek@gmail.com","avatar":"https://avatars.githubusercontent.com/u/16826774?v=4"},"body":"On Tue, Nov 21, 2023 at 4:36 PM Craig H Maynard <chmaynard@me.com> wrote:\n> [git checkout and git switch treat --orphan differently]\n>\n> Leaving aside the question of whether or not this is a bug,\n\nJust to answer the implied question: this is intentional.\n\n> there doesn't appear to be any formal definition of the term \"orphan branch\"\n> in the git documentation. Am I missing something?\n\nWhether it's documented anywhere or not, it's not done well. This is\nnot surprising: It is hard to do it well!  Git uses two phrases for\nthis: \"orphan branch\" and \"unborn branch\". To understand them\nproperly, let's start at the real beginning.  Bear with me for a\nmoment here.\n\nIn Git, the identity of a commit -- the way that Git locates the\ncommit internally -- is its hash ID.  (Aside: until the SHA-256\nconversion, therewas only ever one hash ID for any commit ever made\nanywhere.  Now that Git supports both SHA-1 and SHA-256, there are two\npossible IDs, depending on which scheme you're using.)  It's possible,\nat least in theory, to use Git without ever creating a branch name:\nall you have to do is memorize these random-looking hash IDs.  But\nthat's not how people's brains work, and it's quite impractical.  So\nGit offers us branch names, like \"main\" or \"master\", \"dev\" or\n\"develop\", and so on.\n\nIn Git, a branch name is just a human-readable name for one of Git's\ninternal hash IDs, with a special and very useful property that\ndistinguishes it from a tag name.  Each tag name is a human-readable\nname for a hash ID too; they just lack the special property of the\nbranch names.  We won't get into all the properties here though, and\nfor the moment, we just need to know that the name stands in as a\nmemorable version of the ID.\n\nAs a result, a Git branch name literally cannot exist unless it\nidentifies one specific commit!  We call that one specific commit the\n\"tip commit\" of that branch (which introduces a whole new confusion,\nof whether a \"branch\" is *one commit* or *many commits*, but again we\nwon't get into this here).\n\nThis leaves us with a big \"chicken or egg\" problem\n(https://en.wikipedia.org/wiki/Chicken_or_the_egg).  Suppose we've\njust created a new, empty repository, which by definition has no\ncommits in it: it's *new*, and *empty*.  How many branch names can we\nhave in this new, empty repository?  We've just claimed that a branch\nname must identify some specific commit, and we have no commits, so\nthe answer is: none.  We cannot have any branch names at all.\n\nBut -- here's the other paradox -- whenever we make a *new* commit,\nit's to be *added on to the current branch*.  But we have an empty\nrepository, which cannot have any branch names, so how do we know what\nthe \"current branch\" even *is*?\n\n** Unborn Branch is the better term **\n\nNow that we understand the basic problem -- that a new repository\ncan't have any branches, but that we want Git to *create* a branch\nwhen we make that very first commit -- we can see what an \"orphan\" or\n\"unborn\" branch is all about.  It papers over our chicken-or-egg\nproblem.  We simply save the *name we want Git to create* somewhere,\nthen we make a new commit as usual.  When, eventually, we do make that\ncommit, Git says: \"OK, I should add this new commit to the current\nbranch br1\", or whatever the name is.  Git then creates the new commit\n*and* creates the branch name, all as one big operation.  Now the\nbranch exists: it's born.\n\nWhen we have a normal (not-unborn) branch and create a new commit, Git\ncreates the new commit as usual and then -- here's the unique property\nof *branch names* that makes them so special -- *updates* the branch\nname to hold the new commit's new hash ID.  Git also makes sure that\nthe new commit we just made links back to the commit that *was* the\ntip commit of the branch, just a moment ago.  So this is how branches\n\"grow\" as you make commits.  The *name* holds only the *last* commit\nhash ID.  Each commit holds the *previous* hash ID, so that Git can\nstart at the end of a branch and work backwards.  The previous, or\nparent, commit, has its own parent, which has another parent, all the\nway back to the beginning of time.\n\nThis is also where the dual meaning of \"branch\" clears up somewhat: a\nbranch is both the tip commit *and* the whole-chain-of-commits,\nstarting at the tip and working backwards.  How do we know which\nmeaning someone means?  Sometimes it's clear from context.  Sometimes\nit's not clear.  Sometimes whoever used the word isn't even aware of\nthe issue!\n\n** The `--orphan` options **\n\nThat weird problematic state for a *new* repository, where no branches\ncan exist, yet you want to be \"on\" the branch you're going to create,\nonly exists as a problem for a new and empty repository.  But given\nthat Git has to solve that problem, Git can let you enter that weird\nstate any time.  That's what `--orphan` was originally invented for:\nto go back into that state even if you have some commits.\n\nThat is, `git checkout --orphan` meant: make the current branch name\nbe an unborn branch, the way it is in a new and totally-empty\nrepository.  Then when I make my next commit, that will create a new\ncommit that has no parent commit.  Whether (and when and how) this is\nactually useful is another question entirely, as is the reason for\nswitch and checkout behaving differently in terms of how they treat\nthe index and working tree.  But this is the heart of the option: it\nmeans \"go into the unborn branch state\".\n\n(Side note: there are other ways to solve the \"new repository\"\nproblem, and there are other ways to define \"branch\".  Other version\ncontrol systems sometimes use other ways.  Git's rather peculiar\ndefinition of branch was rare, perhaps even unique, in the early days\nof Git.)\n\nChris\n"},{"id":"485084","messageId":"xmqqwmu79ac4.fsf@gitster.g","threadId":"60543","inReplyTo":"CAPx1Gvf_meaEBq9XfS9aPg0yLja-2sAW5SUg0jx6f1jNyfmWHw@mail.gmail.com","subject":"Re: Orphan branch not well-defined?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-11-24T02:03:23Z","receivedAt":"2023-11-24T02:03:28Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Chris Torek <chris.torek@gmail.com> writes:\n\n> ** Unborn Branch is the better term **\n\nYes,  To orphan is a verb that denotes the act of becoming on an\nunborn branch, and a few references to \"orphan branch\" in our\ndocumentation are misuses of the word, I would have to say.\n\nI suspect that there are other mentions of \"orphan branch\" in the\ncode comments, variable names, and even end-user facing messages,\nsome of which may need to be corrected, but the first place to start\nis in the glossary.  How about this?\n\n Documentation/glossary-content.txt | 19 +++++++++++++++++++\n Documentation/config/advice.txt    |  2 +-\n Documentation/git-checkout.txt     |  2 +-\n Documentation/git-switch.txt       |  2 +-\n Documentation/git-worktree.txt     |  4 ++--\n 5 files changed, 24 insertions(+), 5 deletions(-)\n\ndiff --git c/Documentation/glossary-content.txt w/Documentation/glossary-content.txt\nindex 59d8ab8572..bbf7b84ab7 100644\n--- c/Documentation/glossary-content.txt\n+++ w/Documentation/glossary-content.txt\n@@ -312,6 +312,13 @@ This commit is referred to as a \"merge commit\", or sometimes just a\n [[def_octopus]]octopus::\n \tTo <<def_merge,merge>> more than two <<def_branch,branches>>.\n \n+[[def_orphan]]orphan::\n+\tThe act of becoming on an <<def_unborn,unborn>> branch.\n+\tAfter such an operation, the <<def_HEAD,HEAD>> points at a\n+\t<<def_branch,branch>> that does not yet exist, and the\n+\tcommit first created from such a state becomes a root\n+\tcommit, starting a new history.\n+\n [[def_origin]]origin::\n \tThe default upstream <<def_repository,repository>>. Most projects have\n \tat least one upstream project which they track. By default\n@@ -695,6 +702,18 @@ The most notable example is `HEAD`.\n \tobject,\n \tetc.\n \n+[[def_unborn]]unborn::\n+\tThe <<def_HEAD,HEAD>> can point at a <<def_branch,branch>>\n+\tthat does not yet have any <<def_commit,commit>> on it, and\n+\tsuch a branch is called an unborn branch.  The most typical\n+\tway users encounter an unborn branch is by creating a\n+\trepository anew without cloning from elsewhere.  The HEAD\n+\twould point at the 'main' (or 'master', depending on your\n+\tconfiguration) branch that is yet to be born.  Also some\n+\toperations can get you on an unborn branch with their\n+\t<<def_orphan,orphan>> option.\n+\n+\n [[def_unmerged_index]]unmerged index::\n \tAn <<def_index,index>> which contains unmerged\n \t<<def_index_entry,index entries>>.\ndiff --git c/Documentation/config/advice.txt w/Documentation/config/advice.txt\nindex 2737381a11..4d7e5d8759 100644\n--- c/Documentation/config/advice.txt\n+++ w/Documentation/config/advice.txt\n@@ -140,6 +140,6 @@ advice.*::\n \t\tAdvice shown when a fast-forward is not possible.\n \tworktreeAddOrphan::\n \t\tAdvice shown when a user tries to create a worktree from an\n-\t\tinvalid reference, to instruct how to create a new orphan\n+\t\tinvalid reference, to instruct how to create a new unborn\n \t\tbranch instead.\n --\ndiff --git c/Documentation/git-checkout.txt w/Documentation/git-checkout.txt\nindex 240c54639e..26ad1a5e27 100644\n--- c/Documentation/git-checkout.txt\n+++ w/Documentation/git-checkout.txt\n@@ -215,7 +215,7 @@ variable.\n \tbelow for details.\n \n --orphan <new-branch>::\n-\tCreate a new 'orphan' branch, named `<new-branch>`, started from\n+\tCreate a new unborn branch, named `<new-branch>`, started from\n \t`<start-point>` and switch to it.  The first commit made on this\n \tnew branch will have no parents and it will be the root of a new\n \thistory totally disconnected from all the other branches and\ndiff --git c/Documentation/git-switch.txt w/Documentation/git-switch.txt\nindex c60fc9c138..3e23a82cf2 100644\n--- c/Documentation/git-switch.txt\n+++ w/Documentation/git-switch.txt\n@@ -171,7 +171,7 @@ name, the guessing is aborted.  You can explicitly give a name with\n \t`branch.autoSetupMerge` configuration variable is true.\n \n --orphan <new-branch>::\n-\tCreate a new 'orphan' branch, named `<new-branch>`. All\n+\tCreate a new unborn branch, named `<new-branch>`. All\n \ttracked files are removed.\n \n --ignore-other-worktrees::\ndiff --git c/Documentation/git-worktree.txt w/Documentation/git-worktree.txt\nindex 93d76f5d66..2a240f53ba 100644\n--- c/Documentation/git-worktree.txt\n+++ w/Documentation/git-worktree.txt\n@@ -99,7 +99,7 @@ command will refuse to create the worktree (unless `--force` is used).\n If `<commit-ish>` is omitted, neither `--detach`, or `--orphan` is\n used, and there are no valid local branches (or remote branches if\n `--guess-remote` is specified) then, as a convenience, the new worktree is\n-associated with a new orphan branch named `<branch>` (after\n+associated with a new unborn branch named `<branch>` (after\n `$(basename <path>)` if neither `-b` or `-B` is used) as if `--orphan` was\n passed to the command. In the event the repository has a remote and\n `--guess-remote` is used, but no remote or local branches exist, then the\n@@ -234,7 +234,7 @@ This can also be set up as the default behaviour by using the\n \n --orphan::\n \tWith `add`, make the new worktree and index empty, associating\n-\tthe worktree with a new orphan/unborn branch named `<new-branch>`.\n+\tthe worktree with a new unborn branch named `<new-branch>`.\n \n --porcelain::\n \tWith `list`, output in an easy-to-parse format for scripts.\n"},{"id":"485085","messageId":"xmqqplzz99xr.fsf@gitster.g","threadId":"60543","inReplyTo":"xmqqwmu79ac4.fsf@gitster.g","subject":"Re: Orphan branch not well-defined?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-11-24T02:12:00Z","receivedAt":"2023-11-24T02:12:03Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Chris Torek <chris.torek@gmail.com> writes:\n>\n>> ** Unborn Branch is the better term **\n>\n> Yes,  To orphan is a verb that denotes the act of becoming on an\n> unborn branch, and a few references to \"orphan branch\" in our\n> documentation are misuses of the word, I would have to say.\n\nTo be fair, the use of verb \"orphan\" by the folks first designed the\n\"checkout --orphan\" does make quite a lot of sense and it is very\nmuch consistent with the fact that the operation leaves the index\nand the working tree intact (unlike \"switch --orphan\" that empties\nthe contents, which came much later).\n\nThe intended use case was that the user had the current set of\ncontents that is desirable with history that is undesirable behind\nit, and wanted to part with the baggage^Whistory while keeping the\nend state.  The operation was meant to be the first step to create a\n\"parent-less\" (aka \"orphaned\" from the parents in the original\nhistory) commit that records the desired state.  It is the reason\nwhy \"checkout --orphan\" keeps the contents intact and moves the HEAD\nto be on an unborn branch.\n"},{"id":"485086","messageId":"CAPig+cS6dAANqm7AcrjU9nhBezXRvB0Y-zPOzdar7s_8E-c28Q@mail.gmail.com","threadId":"60543","inReplyTo":"xmqqwmu79ac4.fsf@gitster.g","subject":"Re: Orphan branch not well-defined?","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2023-11-24T02:27:56Z","receivedAt":"2023-11-24T02:28:08Z","isPatch":false,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Thu, Nov 23, 2023 at 9:03 PM Junio C Hamano <gitster@pobox.com> wrote:\n> +[[def_orphan]]orphan::\n> +       The act of becoming on an <<def_unborn,unborn>> branch.\n\ns/on an/an/\n\n> +       After such an operation, the <<def_HEAD,HEAD>> points at a\n> +       <<def_branch,branch>> that does not yet exist, and the\n> +       commit first created from such a state becomes a root\n> +       commit, starting a new history.\n"},{"id":"485087","messageId":"xmqqlean9913.fsf@gitster.g","threadId":"60543","inReplyTo":"CAPig+cS6dAANqm7AcrjU9nhBezXRvB0Y-zPOzdar7s_8E-c28Q@mail.gmail.com","subject":"Re: Orphan branch not well-defined?","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2023-11-24T02:31:36Z","receivedAt":"2023-11-24T02:31:42Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Eric Sunshine <sunshine@sunshineco.com> writes:\n\n> On Thu, Nov 23, 2023 at 9:03 PM Junio C Hamano <gitster@pobox.com> wrote:\n>> +[[def_orphan]]orphan::\n>> +       The act of becoming on an <<def_unborn,unborn>> branch.\n>\n> s/on an/an/\n\nI actually did mean it.  It is not a verb whose subject is a branch.\nThe user (or you can call a repository) gets on a branch that\nhappens not to exist yet.\n"}]}