Caution

The Packaging and Development guide is currently undergoing a major overhaul to bring it up to date. The current state you are seeing now is a preview of this effort.

The current version is unstable (changing URLs can occur at any time) and most content is not in properly reviewed yet. Proceed with caution and be aware of technical inaccuracies.

If you are an experienced packager and would like to contribute, we would love for you to be involved! See our contribution page for details of how to join in.

Making changes to a package

This tutorial goes through the process of adding a patch to a package in Ubuntu. Specifically, we add a command-line option to the hello command, which greets a user using their username. This covers topics like git-ubuntu, quilt, and changelogs.

Getting the tools

We are using a git-ubuntu workflow. Install it with snap:

sudo snap install --classic git-ubuntu

Once we have git-ubuntu installed, use it to fetch the source code for the hello package in Ubuntu:

git-ubuntu clone hello
cd hello/

We are using some tools from the ubuntu-dev-tools package. Install it with:

sudo apt install -y ubuntu-dev-tools

Understanding the package

Initially, we have the ubuntu/devel branch checked out. At the time of writing this tutorial, the development series is plucky, so the ubuntu/devel branch is in line with the plucky version.

Let’s explore the packaging we are dealing with. In the debian directory, there are files like changelog, rules, control, and more. Everything outside of the debian directory is from the original upstream source. In debian/source/format, we see:

3.0 (quilt)

This means that, like most packages, this package uses the quilt tool to manage patches to the upstream source code. So, even though we are using Git to track our changes to the packaging, we need to use a quilt patch to maintain the changes required for our new command-line option.

In particular, instead of ending up with a Git commit that modifies the upstream source code directly, our commit adds a new file, debian/patches/add-username-command-line-option.patch, which contains the patch to apply to the upstream source code.

Creating a patch with quilt

First, create the new patch file using quilt:

QUILT_PATCHES=debian/patches quilt new add-username-command-line-option.patch
Patch add-username-command-line-option.patch is now on top

This should create a new, empty file: debian/patches/add-username-command-line-option.patch. And it adds a corresponding entry to the debian/patches/series file. Once that is done, start writing the patch. For each source file that is modified by our patch, we need to tell quilt about it. In this case:

quilt add src/hello.c

After that, edit the source normally using our favorite text editor. To see our progress, use the usual Git tools to see the diff. So, after adding the new command line flag, the diff might look like:

$ git diff -- src/hello.c
diff --git a/src/hello.c b/src/hello.c
index 453962f..f1ccf0a 100644
--- a/src/hello.c
+++ b/src/hello.c
@@ -23,6 +23,10 @@
 #include "error.h"
 #include "progname.h"
 #include "xalloc.h"
+#include "unistd.h"
+#include "sys/types.h"
+#include "pwd.h"
+#include "limits.h"

 static const struct option longopts[] = {
   {"greeting", required_argument, NULL, 'g'},
@@ -44,6 +48,8 @@ main (int argc, char *argv[])
   const char *greeting_msg;
   wchar_t *mb_greeting;
   size_t len;
+  struct passwd *pwd = NULL;
+  char user_greeting[sizeof("hello, !") + LOGIN_NAME_MAX] = {};

   set_program_name (argv[0]);

@@ -65,7 +71,7 @@ main (int argc, char *argv[])
      This is implemented in the Gnulib module "closeout".  */
   atexit (close_stdout);

-  while ((optc = getopt_long (argc, argv, "g:htv", longopts, NULL)) != -1)
+  while ((optc = getopt_long (argc, argv, "g:htvu", longopts, NULL)) != -1)
     switch (optc)
       {
        /* --help and --version exit immediately, per GNU coding standards.  */
@@ -83,6 +89,15 @@ main (int argc, char *argv[])
       case 't':
        greeting_msg = _("hello, world");
        break;
+      case 'u':
+        errno = 0;
+        pwd = getpwuid(geteuid());
+        if (!pwd)
+          error (EXIT_FAILURE, errno, _("failed to get user name"));
+
+        snprintf(user_greeting, sizeof(user_greeting), "hello, %s!", pwd->pw_name);
+        greeting_msg = _(user_greeting);
+        break;
       default:
        lose = 1;
        break;

To save these changes in our quilt patch, we need to refresh the patch:

quilt refresh -p ab --no-timestamps --no-index

It is good practice to add DEP-3 headers to patches to add additional context, such as the origin, author, and related bugs. The quilt tool has a helper for this:

quilt header -e --dep3

This opens a text editor with pre-populated text:

Description: <short description, required>
 <long description that can span multiple lines, optional>
Author: <name and email of author, optional>
Origin: <upstream|backport|vendor|other>, <URL, required except if Author is present>
Bug: <URL to the upstream bug report if any, implies patch has been forwarded, optional>
Bug-<Vendor>: <URL to the vendor bug report if any, optional>
Forwarded: <URL|no|not-needed, useless if you have a Bug field, optional>
Applied-Upstream: <version|URL|commit, identifies patches merged upstream, optional>
Reviewed-by: <name and email of a reviewer, optional>
Last-Update: 2025-04-23 <YYYY-MM-DD, last update of the meta-information, optional>
---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/

Not everything here needs to be filled in. In this case, our headers might look like:

Description: Add -u command line option to hello
 This command line option adds a username-specific greeting. E.g.,
 $ hello -u
 hello, user123!
Author: Nick Rosbrook <[email protected]>
Forwarded: no, Ubuntu only
Last-Update: 2025-04-23
---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/

Our final patch should look something like:

Description: Add -u command line option to hello
 This command line option adds a username-specific greeting. E.g.,
 $ hello -u
 hello, user123!
Author: Nick Rosbrook <[email protected]>
Forwarded: no, Ubuntu only
Last-Update: 2025-04-23
---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
--- a/src/hello.c
+++ b/src/hello.c
@@ -23,6 +23,10 @@
 #include "error.h"
 #include "progname.h"
 #include "xalloc.h"
+#include "unistd.h"
+#include "sys/types.h"
+#include "pwd.h"
+#include "limits.h"

 static const struct option longopts[] = {
   {"greeting", required_argument, NULL, 'g'},
@@ -44,6 +48,8 @@
   const char *greeting_msg;
   wchar_t *mb_greeting;
   size_t len;
+  struct passwd *pwd = NULL;
+  char user_greeting[sizeof("hello, !") + LOGIN_NAME_MAX] = {};

   set_program_name (argv[0]);

@@ -65,7 +71,7 @@
      This is implemented in the Gnulib module "closeout".  */
   atexit (close_stdout);

-  while ((optc = getopt_long (argc, argv, "g:htv", longopts, NULL)) != -1)
+  while ((optc = getopt_long (argc, argv, "g:htvu", longopts, NULL)) != -1)
     switch (optc)
       {
  /* --help and --version exit immediately, per GNU coding standards.  */
@@ -83,6 +89,15 @@
       case 't':
  greeting_msg = _("hello, world");
  break;
+      case 'u':
+       errno = 0;
+       pwd = getpwuid(geteuid());
+       if (!pwd)
+         error (EXIT_FAILURE, errno, _("failed to get user name"));
+
+       snprintf(user_greeting, sizeof(user_greeting), "hello, %s!", pwd->pw_name);
+       greeting_msg = _(user_greeting);
+       break;
       default:
  lose = 1;
  break;

The patch is currently applied in the working directory.

  • To un-apply: quilt pop -a

  • To apply again: quilt push -a

Committing the changes

Now that we have created our patch file, track the changes in Git. Add the new patch file (and in this case, the newly created debian/patches/series file) to the Git index and commit the change:

git add debian/patches/
git commit -m "debian/patches: add a new -u command line option to hello"

Next, some housekeeping changes:

  1. Make sure that the Maintainer: field in debian/control is set correctly.

  2. Add a new entry to debian/changelog explaining our changes and incrementing the package version number.

To update the maintainer field, use the update-maintainer tool from the ubuntu-dev-tools package. In this case, the field is already set correctly, so we should see:

update-maintainer
The Maintainer email is set to an ubuntu.com address. Doing nothing.

If a change was made, commit that change with:

git commit -m "update maintainer" -- debian/control

Once you have either updated the maintainer, or confirmed that it is already correct, update the changelog. The dch tool helps with this. If you run dch -i, you see something like this in your text editor:

hello (2.10-3ubuntu1) UNRELEASED; urgency=medium

  *

 -- Nick Rosbrook <enr0n@ubuntu.com>  Tue, 22 Apr 2025 17:03:03 -0400

hello (2.10-3build2) oracular; urgency=medium

  * No-change rebuild to bump version in oracular.

 -- Marc Deslauriers <marc.deslauriers@ubuntu.com>  Mon, 27 May 2024 07:18:24 -0400

hello (2.10-3build1) noble; urgency=high

  * No change rebuild for 64-bit time_t and frame pointers.

 -- Julian Andres Klode <juliank@ubuntu.com>  Mon, 08 Apr 2024 17:58:52 +0200

 [...SNIP...]

The dch tool has done a few things:

  1. Created a new empty changelog entry.

  2. Set the author line using your name, email, and the current date and time.

  3. Updated the package version number to 2.10-3ubuntu1.

  4. Set the release name to UNRELEASED.

Our job now is to fill in the entry and explain our changes. In this case, write something like:

hello (2.10-3ubuntu1) plucky; urgency=medium

  * debian/patches: add a new -u command line option to hello

 -- Nick Rosbrook <enr0n@ubuntu.com>  Tue, 22 Apr 2025 17:03:03 -0400

Once you are happy with the changelog entry, commit the change:

git commit -m "update changelog" -- debian/changelog

At this point, we should have two (or three if update-maintainer was needed) commits: one adding our new patch, and another updating the changelog:

git log
commit a62e1590cc6a12925c8fe9bce49d9b7f5834468e (HEAD -> ubuntu/devel)
Author: Nick Rosbrook <[email protected]>
Date:   Wed Apr 23 10:04:32 2025 -0400

    update changelog

commit d6ef1607ce6163e6a611c591e94f478c2c06a35a
Author: Nick Rosbrook <[email protected]>
Date:   Tue Apr 22 16:24:39 2025 -0400

    debian/patches: add a new -u command line option to hello

commit fd73db6d7406ee1fb8512a5b54c259f3b3368eab (tag: pkg/import/2.10-3build2, pkg/ubuntu/plucky-devel, pkg/ubuntu/plucky, pkg/ubuntu/oracular-proposed, pkg/ubuntu/oracular-devel, pkg/ubuntu/oracular, pkg/ubuntu/devel, pkg/HEAD)
Author: Marc Deslauriers <[email protected]>
Date:   Mon May 27 07:18:24 2024 -0400

    2.10-3build2 (patches unapplied)

    Imported using git-ubuntu import.

Notes (changelog):
      * No-change rebuild to bump version in oracular.

And that’s it! We have successfully:

  • Added a new patch to this package.

  • Documented our change.

  • Prepared the package for its next upload to the Ubuntu archive.

Next steps

From here, there are many options for testing our patch before proposing the change in a merge proposal:

  • Build and test the package locally using sbuild and autopkgtest.

  • Upload to a PPA and test from there.

Once you feel confident that the patch is working correctly, open a merge proposal and request Sponsorship for your change.