10

I want to create a repository [B] that tracks a remote repository's master [A] in a branch called x_master. Its own master should also be a clone at the initial creation time that others [Devs] can clone and push changes into.

Occasionally, as there are changes in A, I will need to be able to pull them down and merge them into B's x_master (which, if I understand this, should be a fast-forward as they will be the only changes in x_master branch on B), and then be able to merge those changes into B's master, and thus onto anyone cloning B's master when they pull.

What I conceptually want is this:

master      x_master
 [A] <---------> [B] <-------> [Dev2]
                  ^-------> [Dev1]
                  master

Eventually I'd need to push changes in B's master up to A's master when all the dev is done, but there will be changes going on in A that need to be merged into B

  • How do I set this up?
  • How do I push and pull from B into and from A?
  • Does this setup make sense?

I've tried all kinds of clone --mirror, branch --track, and just don't seem to get the changes in A and B pushing and pulling correctly.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Mark Fisher
  • 9,838
  • 3
  • 32
  • 38
  • Ok, i tried to figure out why you would like to do so, but I don't get it. Why do you want to have 2 repos? What's the difference between them? – Scolytus Oct 20 '11 at 20:26
  • +1. Why do you need two repos? This line of thinking along with other ideas I've seen for using blessed repositories are complete nonsense when you're using git. If you want to have a staging area, use a branch called staging. Use branches for everything you need to separate. – sciritai Oct 20 '11 at 20:35
  • A is a repository I don't have control over on an external system, cannot push changes onto until we have completed our dev, but need to merge changes from. – Mark Fisher Oct 20 '11 at 21:46
  • @Mark: If my answer doesn't satisfy you, please tell me what you want to have in more detail. – Scolytus Oct 20 '11 at 22:16
  • This is much easier in Bazaar. – Tom Anderson Oct 21 '11 at 13:07
  • What is the ultimate source of the changes in A? If B is, as i infer from the names of its downstreams, the repository used by the development team, then who is pushing into A? – Tom Anderson Oct 21 '11 at 13:12
  • I will be manually once dev is complete, we'll need to push to their master for them to build in their environment. However, looks like our builds will be the main source for their QA now, so I'd rather break the dependency of the [A] repository completely. Given it's taking you-know-who several weeks to even give us user accounts that work to create a connection to the repo, I think the less we rely on it the better. – Mark Fisher Oct 23 '11 at 16:05
  • I think there could be lots of reasons why you might want a hierarchy of repositories. As the OP mentioned, one reason is to provide a hierarchy of control. For me, it is about infrastructure and bandwidth. My dev box doesn't have direct access to the master server, and furthermore, access to the remote server can be slow so I want to share the fetches from master among multiple local working repositories. – Brent Bradburn May 17 '12 at 21:49

4 Answers4

8

I am sure there is a shortcut for it, but I tend to just use basic commands. In any case, set up repository for B:

$ cd repo_B
$ git init --bare
$ git remote add upstream URL_FOR_REPO_A
$ git fetch upstream +master:refs/heads/x_master
$ git branch master x_master

When upstream repository is modified, you need to pull those changes in on the bare repository1:

$ git fetch upstream +master:refs/heads/x_master

This will overwrite2 any possible changes in x_master, so you'd better leave that branch alone. :)

You will want to merge upstream changes in x_master into master when/if A changes. Unfortunately, there may be conflicts at this stage, so it must be done with a clone of our bare repository. Simply clone the B repository (to a local or a remote location), and merge x_master into master, resolve the conflicts, and push back.

And the final task is pushing development done in master to repository A. This can be done in two ways. The first is by directly pushing B's master to repository A. This can be done by running:

$ git push upstream

on repository B. An alternative is a more controlled merge from master to x_master using a third repository:

$ git clone URL_FOR_REPO_A
$ cd repoDir
$ git remote add dev URL_FOR_REPO_B
$ git fetch dev
$ git branch --track master_b dev/master
$ git merge master_b
$ <resolve conflicts, if any>
$ git push origin master

Note 1

For completion, you can configure the remote to only fetch that branch by default:

$ git configure branch.upstream.fetch +master:refs/heads/x_master

And with --add, you can even add more branches to fetch:

$ git configure --add branch.upstream.fetch +branch_1_0:refs/heads/x_branch_1_0

Now, fetch will work properly without refspecs:

$ git fetch upstream

Note 2

To prevent pushes to master of repo_B, you can use a server-side hook like pre-receive or update.

Alberto
  • 5,021
  • 4
  • 46
  • 69
vhallac
  • 13,301
  • 3
  • 25
  • 36
  • your first block of statements fail when B is bare at `git checkout master` because there's no working directory. I tried cloning B at this point, but get "remote HEAD refers to nonexistent ref". I believe I need B to be bare because people are pushing into it. – Mark Fisher Oct 21 '11 at 07:50
  • @MarkFisher Oops. I tried this with non-bare, and assumed just making a bare repository would do the trick. I will update the answer to work with bare. – vhallac Oct 21 '11 at 07:57
  • excellent, I was able to pull changes from A into B:x_master, and push up from devs to B:master, and found I could push changes up from B:x_master to A:master by going into B:bare and doing `git push upstream`. If you add this last part to your answer, i'll accept it (to keep it out of comments). Many thanks. – Mark Fisher Oct 21 '11 at 09:51
1

I was inspired, but also somewhat confused by vhallac's answer. I think something simpler may work just as well...

mkdir (name).staging.git ; cd (name).staging.git
git init --bare
git remote add origin (remote-repo)

Periodically (including initially), you will want to fetch changes and update the local branches...

git fetch
for remote in $(git branch -r) ; do git branch --force --no-track $(basename $remote) $remote ; done

Note that it is necessary to update the local branches every time you fetch, otherwise the third-level repo won't see the upstream changes. My example updates all local branches, but you could instead do this manually for only the branches of interest. I suggest creation of an alias "git staging-fetch" that combines these two lines into a single command.

Pushing from the staging repository to the master repository is straightforward as long as there is no merge required (just do a regular push). If the push fails due to conflicts, then you need to fetch the updates through both levels and resolve things in the working repository.


Note: This scheme does not require a branch called "x_master". Git has built-in support for using a local branch to shadow a remote branch. So all "master" branches are just called "master", but when referring to the branch in the immediately upstream repository, you would call it "origin/master" or "staging/master" as the case may be.

branch:       origin master    staging master    working master
              =============    ==============    ==============
origin sees:  master
staging sees: origin/master    master
working sees:                  staging/master    master
Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
  • Note: This is different from simply using `git clone --bare (remote-repo)` to create the staging repo. With `clone --bare` you lose the remote branch references, but by adding the remote after establishing `--bare`, you keep them. I don't know if this difference in behavior is intentional, but fortunately Git does provide a way to achieve what we need here. – Brent Bradburn Oct 31 '12 at 06:26
0

The best way would be to have one responsible developer (call him dev0) who will do the stuff. He has to have read and write access to A. He can set A as a remote in his local repo and maintains B/x_master by pulling commits from A/master to his local x_master and pushing it to B. As you mentioned, that should be easy and always fast-forward. (So it may be scripted)

As B/x_master is also visible to all of the developers anybody can merge (and see/fix conflicts) x_master to master when they feel the need to integrate it to development.

When development is done, dev0 will merge master to x_master. This needs the supervision of a human anyhow, because of possible conflicts. He has also the chance to cancel the merge at this point and do additional commits to reflect the conflict resolving in your development branch. So you can also discuss them in group. When that is done, dev0 can again try to merge master to x_master. Afterwards x_master is pushed to A/master.

Scolytus
  • 16,338
  • 6
  • 46
  • 69
  • developers will be pushing (via Gerrit) into master on B. The complication here is that we need early exposure to changes in A that might affect us, so I need to see those changes and merge them into B in a controlled fashion while still doing dev against our main line. I'm going to be building instances based on x_master and master in B every night so that we can have QA on both, and so we can see if external changes are breaking builds. Due to time constraints we cannot afford to let changes in x_master taint our main line, but we also cannot afford to wait till the end to merge them in. – Mark Fisher Oct 21 '11 at 08:02
0

I assume both A and B are bare repositories used only for pushing and pulling with, right?

I'd argue the simplest way would be to simply have an intermediate repository between A and B. That is, a non-bare one, with a working tree. That way, you can pull and merge from any of the branches in A and B in complete peace and isolation from everyone else, fine-tune the merges until you are entirely happy with them, and then push them to either one of A and B.

Of course, if you already have a working copy, you could simply use that one as that intermediate repository. There's no particular need to create an explicit one; just do all your intermediate work in temporary branches.

Dolda2000
  • 25,216
  • 4
  • 51
  • 92
  • yes, both repositories are bare. In fact, B is controlled by Gerrit, so people will be pushing into refs/for/master before merging into master, but that doesn't actually matter to the problem here, just emphasises that it needs to be bare. – Mark Fisher Oct 21 '11 at 07:51