El 18/4/2008, a las 11:44, "Pat Maddox" <pergesu at gmail.com>
escribi?:
> I believe that pull-merge-commit would work fine, I experimented
> locally to understand the effects of handling submodule reference
> merge conflicts. As I mentioned before, it is just a bit of a hassle
> to have to do. David also pointed out that even without the
> conflicts, you still have to commit the reference, leading to lots of
> "updated rspec-rails" type commits in rspec-dev.
Yes, it sounds like the whole submodule approach was a poor match for
RSpec''s organizational structure. I think submodules are really
intended for super-projects which aggregate other _independent_
projects for easy consumption by others. Note that the emphasis is on
_consumption_. So as you''ve discovered, while cloning rspec-dev.git
and getting all the submodules is nice, it''s not so convenient when it
comes to actually contributing back. Submodules are really optimized
for situations where Project A includes Projects X, Y and Z as
submodules, the developers of X, Y and Z mostly work _within_ those
projects, and the super-project is only occasionally updated.
When I started using Git the whole submodule thing wasn''t exposed yet,
so it wasn''t something I had to consider, but it was already a problem
I''d faced previously when I experimented with using SVK as a layer on
top of SVN (I was just too sick of SVN''s lack of merge tracking). SVK
doesn''t have svn:externals support, either, you see. I ended up going
for the simplest possible solution: symlinks (you can''t nest one SVK
checkout within another so I really did need to keep them in separate
locations).
And when I moved to Git I just continued on with the same arrangement,
which has worked fine. Although your end solution differs in the
details (a rake task which effectively does a few nested clones), the
end result is not all that different.
The key point is that rspec-dev.git is not actually a "product"
intended for consumption by anyone other than people actually working
on RSpec itself. It''s just a convenience for grouping together a bunch
of other projects (which really _must_ belong in repositories of their
own because they are consumed by end-users independently of one
another) and doing stuff like running a "pre_commit" task to make sure
that everything is working.
> pull-merge-commit is probably a good workflow (and indeed the only
> one, because otherwise it''s push-REJECTED-pull-merge-commit).
Or fetch-then-rebase-then-commit
pull by itself is actually fetch-then-merge
If you rebase often (and you probably should in a project like this
were there are multiple contributors) then you can do "pull --rebase"
as a shortcut for fetch-then-rebase with a recent-enough version of Git.
Pat, did you get that email I sent you off-list with the more detailed
explanation of rebase? Pasting it in below as it may be of use to
others.
Cheers,
Wincent
El 10/4/2008, a las 8:52, "Pat Maddox" <pergesu at gmail.com>
escribi?:>
> Thanks for that. I wrote what''s up on the wiki... and to be
perfectly
> honest, I only care about the end goal of having clean histories to
> merge into RSpec core. I also don''t fully understand everything
you
> wrote. If you had some time, perhaps you could take the existing
> content, add some concrete examples of your techniques, and extract
> all of that to a new page. I would certainly be very grateful :)
I don''t have a github account at this stage but here folllows a write-
up that you could use to start a new page.
Cheers,
Wincent
Using topic branches when contributing patches
A "topic" branch is a separate branch that you use when working on a
single "topic" (a bug fix, a new feature, or an experimental idea).
Working on a topic branch instead of directly on top of "master" is
recommended because:
- it allows you to work on multiple, separate topics simultaneously
without having them all jumbled together in a single branch
- topic branches are easily updated, which means that if the remote
"master" evolves while you are working on your topic it is easy to
incorporate those changes into your local topic branch before
submitting (which in turn will make your topic apply cleanly on top of
the current "master")
- if you receive feedback on your patches, having all the related
changes grouped together in a topic makes it easy to tweak them and
resubmit
- working on a topic branch makes it easy to refactor a patch series
into a logical, clear, clean sequence, which in turn makes your
contribution easier to review and more likely to be included
So, for all of these reasons it''s recommended to use a topic branch
for preparing submissions even for simple contributions like single-
commit bugfixes and the like.
Basic topic branch workflow
Let''s assume you are working in a clone of the official repository. If
you set this up using a standard "git clone" then your local
"master"
branch will be set up to track the remote "master" branch
automatically.
# make sure that you''re on your local "master" branch
git checkout master
# integrate latest changes from upstream
git pull
# create and switch to new topic branch named "my_topic"
# (obviously you''d pick a more descriptive name)
git checkout -b my_topic
# now work on your submission, committing along the way
...
# when ready, make a patch series for attachment to a lighthouse
ticket
git format-patch master
That last command will create a numbered series of patch files, one
for each commit on your topic branch (ie. all the commits since you
branched off from "master").
Advanced topic branch workflow
If your topic is long-lived then you can use "git rebase" to keep it
up to date and massage it into shape. Imagine that the remote master
has three commits at the time you start working:
A--B--C
You make a topic branch with three new commits:
A--B--C
\
X--Y--Z
Meanwhile, the remote "master" continues to evolve and has three
commits of its own:
A--B--C--D--E--F
\
X--Y--Z
Without "git rebase", the only way to get your changes into the
"master" branch is for the integrator to perform a merge, creating a
new merge commit, M:
A--B--C--D--E--F--M
\ /
X--Y--Z-
With "git rebase" your changes can be "rebased" on top of
the current
remote "master". Git actually removes your three commits (X, Y and Z),
"fast forwards" your topic branch to incorporate the latest commits
from upstream (D, E and F) and then replays your commits on top of the
new HEAD.
A--B--C--D--E--F
\
X''--Y''--Z''
In this way when you submit your patches they can be applied using a
simple "fast forward" merge which yields a nice, linear history:
A--B--C--D--E--F--X''--Y''--Z''
The content of the commits is the same but I''ve used the X'',
Y'', Z''
notation to indicate that the SHA-1 hashes for them will be different
(because any change in their ancestry will bubble up and change their
identifying hashes).
There is no merge, so your patches are guaranteed to apply without
merge conflicts. If there are any merge conflicts you will have
already resolved them when you ran "git rebase". (This appropriately
shifts the burden of resolving merge conflicts away from the central
integrator onto the contributor; in this way the project can scale to
have many contributors without the integrator becoming a bottleneck.)
To make use of all this, all you have to do is:
# integrate the latest upstream changes into your "master"
git checkout master
git pull
# make sure that you''re on your topic branch
git checkout my_topic
# do the rebase
git rebase master
"git rebase" can do more than just keep your topic branch up-to-date.
Given our example commits X, Y and Z you might want clean up the
history to make it easier to review (because the easier to review, the
more likely it is to be accepted, you''ll receive better feedback, and
bugs are more likely to be caught). By running "git rebase --
interactive" you can:
- remove a commit from a series (you realize that commit "Y"
doesn''t
really belong in the series)
- amend a commit (you realize that the commit message for "X"
isn''t
quite right)
- insert a commit into the series (you realize that you actually need
a "W" commit before "X")
- "squash" several commits into one (you realize that "X"
and "Y" just
make the same change in two different files, and so they logically
belong in a single commit)
- re-order commits (you realize that the series will be easier to
understand if "Z" comes before "X" and "Y")
When you run "git rebase --interactive" you''ll see an editor
window
that allows you to perform all of the above operations in a simple
fashion; you are presented with a list of commits which you can
transform as follows:
- delete a line to drop that commit
- use "pick" (the default) for commits which you want to appear in the
rebased history
- change "pick" to "edit" for each commit you wish to amend
(or
perform additional commits) along the way
- change "pick" to "squash" for each commit which you want
melded into
the previous commit
- reorder the lines to reorder the commits
The rebase will stop automatically for all commits you''ve marked as
"edit", and will provide you with an opportunity to edit the commit
message for commits melded together with "squash". It will also stop
if there are any merge conflicts to resolve. When you are ready to
resume the rebase you just do:
git rebase --continue