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.

8
  • 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? Commented Oct 20, 2011 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. Commented Oct 20, 2011 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. Commented Oct 20, 2011 at 21:46
  • @Mark: If my answer doesn't satisfy you, please tell me what you want to have in more detail. Commented Oct 20, 2011 at 22:16
  • This is much easier in Bazaar. Commented Oct 21, 2011 at 13:07

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

3 Comments

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.
@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.
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.
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 

1 Comment

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.
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.

1 Comment

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.
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.

1 Comment

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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.