Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make orphaned branch from existing repository?

I have a git repository that I have done some testing on and I'd like to include it in the main repository.

I would like to add this as an orphaned branch, because I want to keep it but I don't want to bloat the master branch with my test project.

How can I import the git repository as an orphaned brach? I'd like to keep my commits.

Edit to show it's not a duplicate: I didn't actually care about it being orphaned or anything like that, I just wanted to have a way to merge 2 repositories. The given answer is correct and solves my problem.

like image 724
vrwim Avatar asked May 23 '16 15:05

vrwim


1 Answers

I think you're looking at this wrong. In fact, the existence of this question implies something that is not true. I think what you want is more correctly named "disjoint subgraphs".

To get there, bear with me through this next exercise. If you just want an answer, scroll down. :-)

Exercise: which branch is orphaned?

Imagine you have some repository, and in this repository there are two branches named X and Y (no master, just X and Y). Branch X points to commit a123456 and branch Y points to commit b987654. These commits point to their parents as usual, but if we draw the entire commit graph (the repository is small so this fits nicely in this SO answer) we get:

o--o--o--o    <-- X

o--o--o---o   <-- Y
    \    /
     o--o

There are two different root commits. Branch X points to a123456 which is the tip of a four-commit chain rooted at a000000. Branch Y points to b987654 which is the tip merge commit of a six-commit structure rooted at b000000.

Which branch(es), if any, is an orphan branch?

(This is a good place to stop and think about that question.)


If you chose X, why did you choose that one? If you chose Y, why did you choose that one? If you chose both, I think you could justify that choice; but if you answered neither, I would call that correct and note that you have now agreed with Git.

(If you chose both X and Y, what is your reason for calling these "orphan branches"? I think you could argue for this as a correct answer, and justify it by saying that neither one is connected to master. If we then added a branch named master and made it point to any of the four commits on X, you would then say that branch Y is orphaned and X is not. If you made master point to any of the six commits on Y, you would then say that X is the orphan and Y is no longer orphaned. There are probably more ways to decide, depending on how twisty you want to get with how you define "orphan". But that's not what Git means, and in fact, there is better terminology, which will not disagree with Git, and will serve you better in the long run.)

Disjoint subgraphs

In our exercise example repository above, neither X nor Y was an orphan branch, because both have commits on them. In graph theory—Git repositories are built around graph theory—the two branch names X and Y point to disjoint subgraphs of the overall graph.

Side note: what Git means by orphan branch

In Git, an orphan branch, created with git checkout --orphan newbranch, is a branch in special state, "yet to be born". This state lasts until there are commits on the branch, after which the branch is no longer an "orphan branch". Now it's just an ordinary branch, like any other.

Git actually implements the orphan-branch state in a way that causes the abstraction to leak rather badly. Specifically, it writes the new branch name into HEAD, but nowhere else. Since HEAD contains the name of the current branch, if you check out any other branch, this overwrites the HEAD file, and the orphan branch itself vanishes completely. In other words, only one single branch can ever be in "orphan" state, and it gets there by having its name stored in HEAD and only in HEAD. Since HEAD stores the name of the current branch, only the current branch can be an orphan.

At last, the answer to what I think your question really is

Given two unrelated repositories R1 and R2, you want to make a union repository (possibly a new repository R3, perhaps just modifying one of the two original repositories) whose commit graph is a disjoint graph resulting from the union the two commit graphs in R1 and R2.

Let's make this as a new third repository. (It's even easier to just glom R1 into R2 or vice versa, by omitting some of these steps, so let's start with the full exercise and you can scale it back if you want.)

To build the new repository R3, first, create an empty repository:

$ mkdir r3 && cd r3 && git init

Now fetch everything from R1 into R3. You can do this by adding R1 as a named remote (this is almost certainly the way to go if you are adding R2 into R1, though then we're now in R1 adding R2 rather than in R3 adding R1). You could just run git fetch and give it a URL and use the branch information that it drops into FETCH_HEAD, but let's go with adding both as remotes. The <url>s here can be paths (../../otherrepo.git or /path/to/otherrepo.git), or ssh://... or git://... or https://..., as usual.

Then, fetch everything from R2 into R3. We use exactly the same steps, just a different remote name and URL:

$ git remote add r1 <url for r1>
$ git remote add r2 <url for r2>
$ git fetch r1; git fetch r2

At this point you are essentially done: you have the union as the commit graph in your new repository. The only thing left to do is to make local branch names pointing to the same commits as some or all of the remote-tracking branches.

Since we named repository R1 r1, all of its branches are in refs/remotes/r1/branch-name. Likewise, since we named R2 r2, all of its branches are in refs/remotes/r2/branch-name. You can refer to these by their shortened versions, r1/whatever and r2/whatever. Let's say r1 has branches master and A and r2 has branches master and B.

Because there is only one A, you can do:

$ git checkout A

and you now have a branch named A pointing to the same commit as r1/A. Moreover, your local A is set to track r1/A: that is, its upstream is r1/A.

The same holds for B.

You can create a new master in r3 but it can only point to the same commit as one of the other two repositories, and you cannot use this shortcut. Let's say you want r3's master to match r2/master:

$ git checkout -b master r2/master

will create it and set it up to track r2/master.

If you don't want this upstream --track setting, create the branches using --no-track.

That is all there is to it: creating these two disjoint subgraphs really is that easy. You just need to know that this is what you are doing, and that fussing about with --orphan is only required if you want to grow a new disjoint subgraph organically, rather than fetching it from some existing repository.

like image 193
torek Avatar answered Nov 20 '22 14:11

torek