Question

I have a github private repository, in which I have been working with a few other (private) collaborators. Let's call it repo-a.

A client just added me to their Organization account. I need to essentially share my repo-a with other developers in that Organization. I am not sure whether "share" is the right term here; maybe I need to "copy"? How do I go about doing this?

The more important issue that I hope someone can help is I don't want the Organization to see the history committed by my private collaborators. But, hopefully, I will be able to easily "push" the latest code in repo-a to the Organization (I am still planning to work privately with the repo-a collaborators). Is this reasonably possible?

Was it helpful?

Solution

Short answer: no, it's not reasonably possible.

You're setting yourself up for a lot of trouble here. If the history you give them doesn't contain the actual history, then by definition you have no record of what commits their version includes. No matter what, you will never be able to push from a branch that includes the "private" commits to one that doesn't. You're basically asking to have history that looks like this:

- W - X - ABCD - Y - Z - EFGH - ... (public)
       \              \
        A - B - C - D - x - E - F - G - H (private)

where ABCD is a commit containing the changes of commits A, B, C, and D, and similarly EFGH hides away E, F, G, and H. You can do this using git checkout public-branch; git merge --squash private-branch. A squash merge performs the merge, then records it as a regular commit, i.e. without the merged branch as a parent. This option might work for you, as long as you're very careful with the merging. It's still a pain, though. (Note that merging Y and Z results in duplicate commits in the private repo, ABCD vs A-B-C-D. This is ugly, but better than the alternative, which is completely divergent history.)

So no matter what, your history will diverge. The best case is probably for you to use tags to help yourself keep track of things. For example, you could tag commit D as private-0001 and commit ABCD as public-0001, commit H as private-0002 and commit EFGH as public-0002. That would at least let you have some record of how the separate histories are tied together. You can never actually merge any of the private commits into the public branch, since commits bring with them their ancestors.

So I'd recommend finding another way. There are several options, depending on your reasons for wanting these contributions kept private:

  • Abandon your plan, and make the private contributors public.

  • Have the private contributors commit in your name, or using fake names. (Have them set user.name and user.email in the .gitconfig of this repository.) If all you're trying to do is keep them anonymous, this would be sufficient and painless.

  • Do it halfway. Looking back at the first diagram, after you do your squash merges, have all the private contributors reset to that point (rebasing any other branches they might have). Every time you publicly publish, you essentially throw away the private commits, and regard the squash-merge commit as the new truth. This will require vigilance on your part, making sure the other contributors do what's required, but it would spare you the baggage of all that crazy history. (If someone fails to reset, it is fixable. Suppose they made commit E on top of commit D instead of commit ABCD. They could use git rebase --onto commit-ABCD commit-D to transplant their branch where it belongs.) Your history would end up like this:

    - W - X - ABCD ------------------ Y - Z - EFGH - ...
       \                               \
        A - B - C - D (abandoned)       E - F - G - H (abandoned)
    

    The benefit of this is that you never have any horribly gnarly merges spanning a lot of history, and you don't drag around any baggage in the form of extraneous merges or duplicate commits. If you publicly publish the private contributions infrequently enough, it wouldn't even be that painful. You could even let things get a little asynchronous if you're careful, for example:

    - W - X - AB - X - CD - Z - ...
          |\
          | A - B (abandoned)
           \
            C - D (abandoned)
    

    Just be very, very careful to keep track of what you've squash-merged. Besides tagging as I discussed earlier, you could also have your squash-merge commits include a list of short SHA1s and commit subjects from the merged commits.

OTHER TIPS

The more important issue that I hope someone can help is I don't want the Organization to see the history committed by my private collaborators.

A Git repository comes with all of its history. Every time. You could "export" the current state of your repository by deleting the .git/ folder from it, then running git init again. This would sort of reset your history, though it would be a completely different repository at this point.

Assuming you do that, you could then just push this new repository to a repo on that organization account and be essentially starting from a snapshot of your old repository.

Hope I understood your question right.

I ended up creating a new repo repo-b and sync it with the Organization account. To "incorporate" the new change(s) from repo-a to repo-b, I use the rsync command. For your reference, see this accepted answer for the exact command.

It then depends on my discipline / flow when to update the Org repo. I could sync each time there is a new commit in repo-a or wait until there are several of them (essentially like squash merge). This way, the Org account can see the history of my commits while keeping my private collaborators anonymous.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top