Oct 15, 2010
Big part of my job is packaging for Fedora Linux (I am pretty sure I haven't mentioned this before :-) ). I have spent last 6 months working on various Java packages, adding new packages to Fedora, updating dependencies etc. I have developed certain workflow which I believe might be of interest to other packagers. So here goes. Most of these hints are about managing patches for your packages. I'll also try to work on concrete package so it won't be completely theoretical.
Let's assume your project already has some history and patches. Let's fix velocity bug 640660 for example. I'll start with steps I took and what they meant, and I'll summarize in the end with rationale what I have gained by using my workflow (and what could be improved).
After modifying BuildRequires and Requires to tomcat6 servlet api I tried to build velocity:
Now we can easily work in our new git repository, edit source file in question and do:
Now that we have patch prepared for velocity we need to use it in the spec file and we're done.
Let's say our first attempted patch wouldn't work as expected and build (or test) still failed. We modify the sources again and do another commit. What we have now is:
We don't want two patches in the spec file for one fix so: time for git magic. You've probably heard of git rebase if you've been using git for a while. What we want to do now is merge last two commits into one, or in git-speak "squash" them. To do this you have to do:
So we just need to change "pick c15f7e0 Fix previous commit" into "squash c15f7e0 Fix previous commit" (you can also use just 's' instead of 'squash'). Save. Close. Another editor window will open with something like this:
For this case we will delete second message because we just want to pretend our first attempt was perfect :-). Save. Close. Now we have:
Repeat as many times as you want. You can also re-order commits and change commit messages with rebase (note that if you just want to change last commit message you can do "git commit --amend"). I generally don't create commits until I have working patch though.
So why do I think all this mumbo-jumbo improves my workflow? Let's see:
Let's assume your project already has some history and patches. Let's fix velocity bug 640660 for example. I'll start with steps I took and what they meant, and I'll summarize in the end with rationale what I have gained by using my workflow (and what could be improved).
After modifying BuildRequires and Requires to tomcat6 servlet api I tried to build velocity:
$ fedpkg mockThis is what I got:
---snip---- compile-test: [javac] Compiling 125 source files to /builddir/build/BUILD/velocity-1.6.3/bin/test-classes [javac] /builddir/build/BUILD/velocity-1.6.3/bin/test-src/org/apache/velocity/test/VelocityServletTestCase.java:135: org.apache.velocity.test.VelocityServletTestCase.MockServletContext is not abstract and does not override abstract method getContextPath() in javax.servlet.ServletContext [javac] static class MockServletContext implements ServletContext [javac] ^ [javac] Note: /builddir/build/BUILD/velocity-1.6.3/bin/test-src/org/apache/velocity/test/VelocityServletTestCase.java uses or overrides a deprecated API. [javac] Note: Recompile with -Xlint:deprecation for details. [javac] Note: Some input files use unchecked or unsafe operations. [javac] Note: Recompile with -Xlint:unchecked for details. [javac] 1 error BUILD FAILED /builddir/build/BUILD/velocity-1.6.3/build/build.xml:251: Compile failed; see the compiler error output for details. Total time: 47 seconds ---snip---The issue seems simple to fix, just missing stub function in a test case, right? So what now?
$ fedpkg prep $ mv velocity-1.6.3 velocity-1.6.3.git $ cd velocity-1.6.3.git $ git init && git add . && git commit -m 'init'This effectively created my small git repository for sources and populated it with all files. Using fedpkg prep step we extracted the tarball and applied already existing patches to unpacked sources. I suggest you create shell alias for last three commands as you'll be using it a lot. We moved directory to velocity-1.6.3.git so that next (accidental?) fedpkg prep won't erase our complicated changes (yes it happened to me once. I've had better days). Note that velocity-1.6.3.git is not a temporary directory. I will keep it around after fixing this bug so that I can use git history, diffs and other features in the future. It is especially nice when you have packages with lot of patches on top.
Now we can easily work in our new git repository, edit source file in question and do:
$ git add src/test/org/apache/velocity/test/VelocityServletTestCase.java $ git commit -m 'Fix test for servlet api 2.5' $ git format-patch HEAD~1This created commit with descriptive message and generated a patch file 0001-Fix-test-for-servlet-api-2.5.patch in our current directory. This is how the patch looks like:
From 8758e3c83411ffadc084d241217fc25f1fd31f42 Mon Sep 17 00:00:00 2001 From: Stanislav Ochotnicky <sochotnicky@redhat.com> Date: Thu, 14 Oct 2010 10:20:52 +0200 Subject: [PATCH] Fix test for servlet api 2.5 --- .../velocity/test/VelocityServletTestCase.java | 7 ++++++- 1 files changed, 6 insertions(+), 1 deletions(-) diff --git a/src/test/org/apache/velocity/test/VelocityServletTestCase.java b/src/test/org/apache/velocity/test/VelocityServletTestCase.java index 824583e..ac0ab5c 100644 --- a/src/test/org/apache/velocity/test/VelocityServletTestCase.java +++ b/src/test/org/apache/velocity/test/VelocityServletTestCase.java @@ -16,7 +16,7 @@ package org.apache.velocity.test; * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations - * under the License. + * under the License. */ import java.io.IOException; @@ -149,6 +149,11 @@ public class VelocityServletTestCase extends TestCase return this; } + public String getContextPath() + { + return ""; + } + public String getServletContextName() { return "VelocityTestContext"; -- 1.7.2.3
Now that we have patch prepared for velocity we need to use it in the spec file and we're done.
Let's say our first attempted patch wouldn't work as expected and build (or test) still failed. We modify the sources again and do another commit. What we have now is:
$ git log --format=oneline c15f7e02eaae93b755cc0bfde6def3d6e67d2b0f (HEAD, master) Fix previous commit 3e3d654c142c7028c9c7895579fba204c4c4bf08 Fix test for servlet api 2.5 2f32554ddf892f4cca3f78b1f82a7c3ab169c357 init
We don't want two patches in the spec file for one fix so: time for git magic. You've probably heard of git rebase if you've been using git for a while. What we want to do now is merge last two commits into one, or in git-speak "squash" them. To do this you have to do:
$ git rebase -i HEAD~2Now your editor should pop-up with this text:
pick 3e3d654 Fix test for servlet api 2.5 pick c15f7e0 Fix previous commit # Rebase 2f32554..c15f7e0 onto 2f32554 # # Commands: # p, pick = use commit # r, reword = use commit, but edit the commit message # e, edit = use commit, but stop for amending # s, squash = use commit, but meld into previous commit # f, fixup = like "squash", but discard this commit's log message # # If you remove a line here THAT COMMIT WILL BE LOST. # However, if you remove everything, the rebase will be aborted. #
So we just need to change "pick c15f7e0 Fix previous commit" into "squash c15f7e0 Fix previous commit" (you can also use just 's' instead of 'squash'). Save. Close. Another editor window will open with something like this:
# This is a combination of 2 commits. # The first commit's message is: Fix test for servlet api 2.5 # This is the 2nd commit message: Fix previous commit # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit.
For this case we will delete second message because we just want to pretend our first attempt was perfect :-). Save. Close. Now we have:
$ git log --format=oneline cbabb6ac43f7bdb8e52ccd09c25cfd0a032b553c (HEAD, master) Fix test for servlet api 2.5 2f32554ddf892f4cca3f78b1f82a7c3ab169c357 init
Repeat as many times as you want. You can also re-order commits and change commit messages with rebase (note that if you just want to change last commit message you can do "git commit --amend"). I generally don't create commits until I have working patch though.
So why do I think all this mumbo-jumbo improves my workflow? Let's see:
- I can have long comments for every patch I create (instead a line or two in spec file)
- I can use the same patches to send directly to upstream
- I don't have to juggle around with diff and remember what files I changed where
- Probably several other things I haven't even realized
I've been doing that even before fedpkg, in the inglorious times of CVS, and everyone I would talk with would tell me how it's more complicated and cumbersome than just using good old diff.
Glad to know I'm not the only nutjob around here. :D
The patch name has never bothered me, but if it matters to you, you can just rename the file to your liking, the patch will remain applyable anyway.
However, I don't use « fedpkg prep » to get the source dir : I install the latest src rpm. This way, I also have all the other patches next to the source dir.