views:

53

answers:

2

I have a repo called MySharedLib, and another repo called MyProject. MySharedLib is included in many different repos by force-pulling (like a Jedi), and NOT using subrepos.

If you clone MyProject, you are left with the following structure:

/MyProject
    MySharedLib
    OtherStuff
    Files...

MySharedLib is not a subrepo. Pulling in changes from MySharedLib is as easy as running:

hg pull -f path/to/MySharedLib.

But if changes are made to /MyProject/MySharedLib, what's the most straightforward/standard way to push ONLY those changes to the MySharedLib repo?

MQ? hg export? hg diff? hg transplant? My understanding is that almost all of these options work (some together, some apart), but I'd like some direction.

And then what happens if a dev makes a commit that includes other files than those within MySharedLib? Obviously this is something to avoid, but I'm just curious.

+1  A: 

Wow, what a bizarre setup. How do you prevent changes to MySharedLib from being pushed to MyProject? How do the files from MySharedLib ever appear unless you do a merge after pulling them? Once you do the merge, the repos are joined and you will need to make advanced used of hg convert (as described in this question about splitting repositories) to pull them apart again.

Do not do this. Use subrepos. This problem is what they exist to solve.

Omnifarious
Thanks for the comment. Using subrepos is actually what I'm trying to avoid, as that's what we have now. It's a big .net project, and the shared libraries we're dealing with are actively developed and are not external. Subrepos have issues showing status and when cloning locally and branching, and so I was trying to make it as easy as possible for the devs. But, if subrepos are the simpliest and most standard way to go, then that is what I will stick with.
Andrew
@Andrew - Have you filed any bug reports or gone onto the user list or IRC to ask about the problems you're having with subrepos? It's not like how they work is set in stone. If there's a problem with them, it's possible they can be fixed. Your method of having multiple parallel 'unrelated' trees in the same repo is... interesting. Mercurial also needs to have a way of working on only part of a project, and a way of working with an incomplete history. I wonder if a way for your method to work might not fall out of that.
Omnifarious
I think the main issue with them right now is when using a central server and having the subs be shared libraries, instead of remote libraries... as in, this library is shared, but work is done in house on it, as opposed to something you use, but never contribute back to. For a newish to mercurial developer, having to deal with subrepos is confusing and error-prone. Once you understand how they work, problems go away, but it takes a long time, and doesn't make convincing my team that "mercurial is awesome" any easier! :) I've read a wiki page awhile back that talked about changes coming...
Andrew
... to subrepos, but I haven't looked in a while to see if those changes were ever implemented, mostly to do with relative paths and cloning. The method I described in my question was mostly just me trying to think of an easier way to manage things than with subrepos, and not something that I was using in production. Either way, I was planning on merging that separate head back in whenever I did a force pull.
Andrew
+2  A: 

Here are the constraints that govern what you can push:

  • you can only push whole changesets -- if you commit some changes together it's all or none on the pushing front, you can't break up a changeset after you commit it
  • you can't push a changeset without pushing all of it ancestor changesets to

So once you've committed a linear history like this:

[0]---[1]----[2]-----[3]

you can push changesets zero and one without pushing two and three, but if you want to push two you also have to push zero and one.

And if changeset one contains changes to both /MyProject/OtherStuff and /MyProject/MySharedLib/ you have to push those together.

Your only flexibility comes before you commit where you can control:

  • what goes into a changeset
  • what the parents of a changeset are (which also have to be pushed with it)

So if your history currently looks like this:

[0]---[1]

and hg status is showing something like this:

M MyProject/OtherStuff/file1
M MyProject/OtherStuff/file2
M MyProject/MySharedLib/file3
M MyProject/MySharedLib/file4

Then you want to make a new changeset that has only the changes for MySharedLib that you want to push:

hg commit --include MyProject/MySharedLib

making your history look like:

[0]----[1]-----[2]

and then, before you commit the changes in OtherStuff you don't want to push you do a hg update to change the current parent revision so that your new changeset will have a parent of one instead of two:

hg update 1

Now when you do:

hg commit

your new changeset, three, will have only the non-MySharedLib changes and a parent of one, so you history will look like this:

[0]-----[1]--------[2]
          \
           --------[3]

Since two and three aren't one another's ancestors you can push either one without pushing the other.

That said, omnifarious is right: your usage isn't just weird, it's out and out wrong. You should look at a subrepo setup. It almost certainly achieves your goals better than what you or I have just described.

Ry4an
Thank you very much for the clear explanation of how to manage multiple heads purposefully. As I said in the comment to omnifarious's answer, I'm currently using subrepos now, and was actually just seeing if there was a better way, since subrepos have some issues when cloning locally, branching, and don't necessarily show their status easily. My entire team is new to Mercurial, and subrepos are just one more thing to teach and handle, and I wanted to be sure that I made the right choice initially. Thanks again for the great explanation.
Andrew
No prob. Subrepos are definitely a little hassle-y right now, but they're only getting better with each release.
Ry4an
Indeed! I think a key insight into subrepos is that using them for shared libraries (meaning you actually contribute to them) is completely different than using them for remote libraries (that you're just pulling in as an API of sorts).
Andrew