ChatGPT解决这个技术问题 Extra ChatGPT

In git, is there a simple way of introducing an unrelated branch to a repository?

While helping a friend with a git problem today, I had to introduce a branch that needed to be totally separate from the master branch. The contents of this branch really had a different origin from what had been developed on the master branch, but they were going to be merged into the master branch at a later time.

I remembered from reading John Wiegley's Git from the bottom up how branches are essentially a label to a commit that follows a certain convention and how a commit is tied to a tree of files and, optionally to parent commits. We went to create a parentless commit to the existing repository using git's plumbing:

So we got rid of all files in the index ...

$ git rm -rf .

... extracted directories and files from a tarball, added those to the index ...

$ git add .

... and created a tree object ...

$ git write-tree

(git-write-tree told us the sha1sum of the created tree object.)

Then, We committed the tree, without specifying parent commits...

$ echo "Imported project foo" | git commit-tree $TREE

(git-commit-tree told us the sha1sum of the created commit object.)

... and created a new branch that points to our newly created commit.

$ git update-ref refs/heads/other-branch $COMMIT

Finally, we returned to the master branch to continue work there.

$ git checkout -f master

This seems to have worked as planned. But this is clearly not the kind of procedure I would recommend to someone who is just getting started using git, to put it mildly. Is there an easier way of creating a new branch that is entirely unrelated to everything that has happened in the repository so far?


C
Community

There is a new feature (since V1.7.2) which makes this task a little more high-level than what's in any of the other answers.

git checkout now supports the --orphan option. From the man page:

git checkout [-q] [-f] [-m] --orphan <new_branch> [<start_point>]

Create a new orphan branch, named , started from and switch to it. The first commit made on this new branch will have no parents and it will be the root of a new history totally disconnected from all the other branches and commits.

This doesn't do exactly what the asker wanted, because it populates the index and the working tree from <start_point> (since this is, after all, a checkout command). The only other action necessary is to remove any unwanted items from the working tree and index. Unfortunately, git reset --hard doesn't work, but git rm -rf . can be used instead (I believe this is equivalent to rm .git/index; git clean -fdx given in other answers).

In summary:

git checkout --orphan newbranch
git rm -rf .
<do work>
git add your files
git commit -m 'Initial commit'

I left <start_point> unspecified because it defaults to HEAD, and we don't really care anyway. This sequence does essentially the same thing as the command sequence in Artem's answer, just without resorting to scary plumbing commands.


Do you know if it is possible to create an orphan branch that stays visible when you checkout any branch from the other "tree"?
@JJD: I think what you want is git merge. ( git checkout master && git merge --no-commit "orphan-branch" ) Some similar tricks will work using git-reset or playing with the index. But it depends on your desired workflow.
@Matthew Here is the changelog for git 1.7.2.
@phord I more or less thought of the orphan to contain things like unit tests or documentation separated from the source code of the project.
@JJD: The script I gave should give you what you want, unless I misunderstood you. When you said "stays visible", do you mean the files will remain in the working directory even though you checked out a different branch? Using --no-commit on git merge will achieve this. You may need to follow up with a git reset origin/master so your next commit goes where you want, but then the files from your orphan-branch will show up as "untracked files" unless you also include them in your .gitignore file.
A
Artem Tikhomirov

From Git Community Book:

git symbolic-ref HEAD refs/heads/newbranch 
rm .git/index 
git clean -fdx 
<do work> 
git add your files 
git commit -m 'Initial commit'

Next answer is better for modern versions of git.
@kikito: Re: "Next answer is better" ... The ordering here on SO is not stable. Could you add a link pointing to what you think is a better answer.
I was referring to stackoverflow.com/a/4288660/312586. You are right, I should not have said "next". It had less score than this answer when I commented.
rm .git/index is ugly :-)
This one works better for case "I want to commit to the branch, creating it if it did not exist before"
J
Jakub Narębski

Although the solution with git symbolic-ref and removing index works, it might be conceptually cleaner to create new repository

$ cd /path/to/unrelated
$ git init
[edit and add files]
$ git add .
$ git commit -m "Initial commit of unrelated"
[master (root-commit) 2a665f6] Initial commit of unrelated
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 foo

then fetch from it

$ cd /path/to/repo
$ git fetch /path/to/unrelated master:unrelated-branch
warning: no common commits
remote: Counting objects: 3, done.
Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
From /path/to/unrelated
 * [new branch]      master     -> unrelated-branch

Now you can delete /path/to/unrelated


In my opinion, a clean concept would involve an option to git branch or git checkout. I'm glad that git makes this sort of stuff possible, but why shouldn't it be easier?
Because it is, and should be a rare thing. If you have unrelated things branch, it usually belongs to unrelated repository, instead of stuffing it in existing one (although there are exceptions).
+1. For fellow git newbies, you can apparently then list the branches via git branch and switch between them via git checkout BRANCH_NAME.
Thanks for this, I wouldn't have thought about it on my own. This is extra helpful because it maintains the history (if there is any) for the other repo.
u
unknownprotocol

The currently selected answer is correct, I would just add that coincidentally...

This is actually exactly how github.com lets users create Github Pages for their repos, thru an orphaned branch called gh-pages. The pretty steps are given and explained here:

https://help.github.com/articles/creating-project-pages-manually

Basically the git commands to set this up are as follows:

git checkout --orphan gh-pages (create a parentless branch called gh-pages on your repo) git rm -rf . (removes any files from branch working tree) rm '.gitignore' (even the gitignore) Now add website content (add index.html, etc) and commit and push. Profit.

Note you can also designate a /docs folder on you repo to be the source of the "Project Site" that Github uses to build the website.

Hope this helps!


D
Daniel Reis

In recent Git versions, 2.27 at least, this can be cleanly achieved with the switchcommand:

git switch --orphan <new-branch>

Official documentation: https://www.git-scm.com/docs/git-switch


G
Greg Hewgill

Github has a feature called Project Pages where you can create a particular named branch in your project to provide files that will be served by Github. Their instructions are as follows:

$ cd /path/to/fancypants
$ git symbolic-ref HEAD refs/heads/gh-pages
$ rm .git/index
$ git clean -fdx

From there you have an empty repository which you can then add your new content to.


S
Sing

Sometimes I just want to create empty branch in project instantly then start doing work, I will just excute following command :

git checkout --orphan unrelated.branch.name
git rm --cached -r .
echo "init unrelated branch" > README.md
git add README.md
git commit -m "init unrelated branch"

V
VonC

If your existing content was already committed, you now (Git 2.18 Q2 2018) can extract it into its own new orphan branch, since the implementation of "git rebase -i --root" has been updated to use the sequencer machinery more.

That sequencer is the one now allowing to transplant the whole topology of commit graph elsewhere.

See commit 8fa6eea, commit 9c85a1c, commit ebddf39, commit 21d0764, commit d87d48b, commit ba97aea (03 May 2018) by Johannes Schindelin (dscho).
(Merged by Junio C Hamano -- gitster -- in commit c5aa4bc, 30 May 2018)

sequencer: allow introducing new root commits In the context of the new --rebase-merges mode, which was designed specifically to allow for changing the existing branch topology liberally, a user may want to extract commits into a completely fresh branch that starts with a newly-created root commit. This is now possible by inserting the command reset [new root] before picking the commit that wants to become a root commit. Example:

reset [new root]
pick 012345 a commit that is about to become a root commit
pick 234567 this commit will have the previous one as parent

This does not conflict with other uses of the reset command because [new root] is not (part of) a valid ref name: both the opening bracket as well as the space are illegal in ref names.


S
ShawnFeatherly

Creating a separate repository worked well for me. Didn't have to worry about a .

Create an new empty repo somewhere else. Rename the master branch to anything other than master, ie gh-pages Start your orphan work. Commit your work to a the only branch in the new repository. Push the new branch to the same remote repo that contains the original master branch.

This method is mentioned on:

https://gitlab.com/tortoisegit/tortoisegit/-/issues/1090#note_77403778

https://gist.github.com/chrisjacob/1086274/382ef1ccc22b57b9b1f0e3a362b39e806b9ba04c


B
Benoît

Found this script at http://wingolog.org/archives/2008/10/14/merging-in-unrelated-git-branches and it works very fine !

#!/bin/bash

set -e

if test -z "$2" -o -n "$3"; then
    echo "usage: $0 REPO BRANCHNAME" >&2
    exit 1
fi

repo=$1
branch=$2

git fetch "$repo" "$branch"

head=$(git rev-parse HEAD)
fetched=$(git rev-parse FETCH_HEAD)
headref=$(git rev-parse --symbolic-full-name HEAD)

git checkout $fetched .

tree=$(git write-tree)

newhead=$(echo "merged in branch '$branch' from $repo" | git commit-tree $tree -p $head -p $fetched)
git update-ref $headref $newhead $head
git reset --hard $headref

I believe that the same effect can be achieved using git fetch $REMOTE $REMOTE_BRANCH:$LOCAL_BRANCH. Am I missing something?