Git question

jdavidb on 2008-04-17T20:14:24

I have a branch named local which is a descendant of master with changes which are local to my machine that I want only in my own working environment, and never anywhere else. I have a branch named work which is a descendant of local and includes not only the local changes which I do not want to commit to our working repository, but also some changes I have worked on and now do want to commit.

I have a branch named stage which is a pristine copy of master. I want to assemble JUST the changes I made in work that I want to commit, lacking the changes I made in local. How do I tell git "merge the differences between local and work into stage; not including any of the changes between master and local"? I know how to use git cherry to get a list of just the commits involved, but I don't know any command that says "Merge just those commits into this branch."

Thanks to anyone for any help you can provide. :)


rebase

rafael on 2008-04-18T05:56:00

Sounds like you want git rebase --onto.

See the docs, there's an example:

http://www.kernel.org/pub/software/scm/git/docs/git-rebase.html

Re:rebase

jdavidb on 2008-04-18T12:31:14

Thanks for the pointer, rafael!

Re:rebase

jdavidb on 2008-04-18T12:46:05

Well, this seems like the right command, and from the documentation this should be working. I'm executing:

git rebase --onto stage local work

My understanding is this should select all of the commits that are in work but not in local, and then apply them all to stage. git crunches some numbers or something, even ran garbage collection, seems to do something ... but my "stage" branch is left completely unchanged, as far as I can tell.

I tried using the -i (interactive) option to git-rebase, and I could clearly see the right list of commits is selected. I just can't get any evidence that it did anything with them, other than the fact that git spends some time doing whatever it's doing.

The documentation also says that if you use the --onto option, the current branch should be reset to the branch you gave as the --onto parameter, but that doesn't seem to be happening, either.

Any chance you or someone else reading can give me any more pointers as to what I'm doing wrong?

I think it's slightly humorous that some of my problems come from the fact that I've been reading about distributed version control for over year, thinking about it for longer than that, and now that I've actually been using it for only a week I'm already trying to benefit from its advanced features. Most of what I see online is geared toward people moving from centralized VCS to distributed who might never even think to do these things. :)

Re:rebase

Aristotle on 2008-04-18T13:00:53

I haven’t actually tried this, but going by the documentation, it seems like your situation is as in the git-rebase --onto master topicA topicB example in the docs. So if I understand correctly, your stage branch would indeed remain untouched – however, after rebasing, the work branch would be rooted on stage instead of local.

To verify, switch to work and check the log.

Re:rebase

jdavidb on 2008-04-18T13:29:03

Hey, you're correct! That's what it did!

I'm still not sure how I was misunderstanding the docs. But, I think I can use this. I can completely drop the concept of the stage branch, I think, and just rebase work branches to master when I am ready to commit them.

I think. Still a little scared. And I'm even more scared that the next thought I had was, "At least I still have CVS to tell me what's changed or not; that should make me feel secure." Seriously. And then I thought, "Yeah, right."

Re:rebase

jdavidb on 2008-04-18T14:28:14

This is great! This is now accomplishing just what I want, although not in the way I expected. Instead of saying "Take the changes between branch A and branch B, and apply them to transform branch C," I can say "Pluck branch B off from its root at branch A and reattach it to new direct ancestor D, eliminating any of the changes between D and A." Works. :)

Re:rebase

Aristotle on 2008-04-18T18:16:59

This is now accomplishing just what I want, although not in the way I expected.

And that is probably why at first you misunderstood it. :-) It happens to me too when I approach something with a strong preconception of what I want to happen when it actually does something different to achieve the same ultimate goal.

Still a little scared.

Yeah – git’s hundreds of yards of rope for rewriting history can be scary. :-)

“Pluck branch B off from its root at branch A […]”

Note that it needn’t even be a formal branch. You can use any two commits where one is an ancestor of the other. That’s the git rebase --onto topicA~5 topicA~3 topicA example in the docs: it says “take the stretch from after topicA~3 (the fourth last commit on topicA) to topicA (the last commit on topicA) and root it onto topicA~5 (the sixth last commit on topicA).” What happens is that the fifth and fourth last commit of topicA become a nameless orphan branch rooted on what was topicA~5, and the three last commits of topicA are also rooted on that commit, so the ancestry of topicA no longer includes the orphan branch.

Note that you can reverse this command if you know the SHA1 of that fourth last commit. You can say something like gitk --all `git-fsck | ack --output='$1' '^dangling commit (\w*)'` to browse all the orphan branches in your repo (as long as you don’t GC them). (The first time you do this you will also discover that every time you rewrite history, even if it is just by amending a commit, you leave behind such orphan branches.) Once you have the SHA1 ID of the commit in question, you can say git rebase --onto $SHA1 topicA~3 topicA, which will root the same stretch of four commits from the previous example back onto that commit.

Re:rebase

jdavidb on 2008-04-18T18:26:39

Note that it needn’t even be a formal branch. You can use any two commits where one is an ancestor of the other.

Gah! Stop; I'm not sure a mere mortal should be trusted with such power! :)

Note that you can reverse this command if you know the SHA1 of that fourth last commit.

I feel a probably unjustified sense of pride at knowing that.

Actually the design of git's internals has fascinated me so much that I've even dreamed about it in the last couple of weeks.