Skip to content

Commit c32329d

Browse files
claudeben-edna
authored andcommitted
docs: expand patching how-to into a mature guide
Add a Prerequisites section explaining that dfetch diff needs a committed upstream baseline before edits are made. Add an "Upgrading the upstream version" section with three explicit outcome paths: clean apply, fuzz-apply (refresh patch), and conflict (manual resolution + update-patch). Add a Troubleshooting section covering the five most common failure messages users encounter with patches. Expand the manifest wiring section to cover patch file organisation conventions and the behaviour of update-patch with multiple patches (always updates the last entry). Expand the format-patch section to document the --output-directory flag. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DsDq9BdiWvfxtL9HMvtmaa
1 parent 0034607 commit c32329d

1 file changed

Lines changed: 203 additions & 21 deletions

File tree

doc/howto/patching.rst

Lines changed: 203 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,44 @@ The full lifecycle looks like this:
2222
1. :ref:`patching-create` — capture local edits as a ``.patch`` file with ``dfetch diff``
2323
2. :ref:`patching-wire` — reference the patch from the manifest so it is applied on every fetch
2424
3. :ref:`patching-update` — refresh the patch as your edits evolve with ``dfetch update-patch``
25-
4. :ref:`patching-upstream` — reformat the patch for upstream use with ``dfetch format-patch``
25+
4. :ref:`patching-upstream-bump` — re-apply your patch when you move to a new upstream version
26+
5. :ref:`patching-upstream` — reformat the patch for upstream use with ``dfetch format-patch``
27+
28+
.. _patching-prereq:
29+
30+
Before you begin
31+
----------------
32+
33+
*Dfetch* calculates the diff for a project by comparing the working tree
34+
against the revision recorded in the project's ``.dfetch_data.yaml`` metadata
35+
file. For that comparison to be meaningful, the fetched files should already
36+
be committed to your superproject's VCS — they become the baseline that the
37+
patch is measured against.
38+
39+
After fetching, commit before editing:
40+
41+
.. tabs::
42+
43+
.. tab:: Git
44+
45+
.. code-block:: console
46+
47+
$ dfetch update some-project
48+
$ git add some-project/
49+
$ git commit -m "vendor: add some-project v1.2.3"
50+
51+
.. tab:: SVN
52+
53+
.. code-block:: console
54+
55+
$ dfetch update some-project
56+
$ svn add some-project/
57+
$ svn commit some-project/ -m "vendor: add some-project v1.2.3"
58+
59+
You can then make edits to ``some-project/`` and capture them with
60+
``dfetch diff``. Both committed and uncommitted edits are included in the
61+
generated patch, so you do not need to commit every intermediate step — only
62+
the clean upstream baseline matters.
2663

2764
.. _patching-create:
2865

@@ -38,11 +75,17 @@ run:
3875
$ dfetch diff some-project
3976
4077
*Dfetch* compares the working tree against the revision recorded in the
41-
metadata file and writes a patch file named ``some-project.patch`` (or
42-
``some-project-N.patch`` if multiple patches already exist).
78+
metadata file and writes a patch file named ``some-project.patch``.
4379

4480
.. asciinema:: ../asciicasts/diff.cast
4581

82+
**What goes into the patch**
83+
84+
The diff captures all tracked modifications and any new untracked files in the
85+
vendored directory. Files ignored by your superproject's VCS (via
86+
``.gitignore`` or ``svn:ignore``) and the ``dfetch`` metadata file itself are
87+
always excluded.
88+
4689
**Controlling which revisions are compared**
4790

4891
By default, *Dfetch* uses the revision stored in the project's metadata as the
@@ -68,8 +111,8 @@ See :ref:`diff` in the command reference for all options.
68111
Adding the patch to the manifest
69112
---------------------------------
70113

71-
Once you have a patch file, reference it from the project entry in
72-
``dfetch.yaml`` using the :ref:`patch` attribute:
114+
Once you have a patch file, commit it to your repository and reference it from
115+
the project entry in ``dfetch.yaml`` using the :ref:`patch` attribute:
73116

74117
.. code-block:: yaml
75118
@@ -82,7 +125,23 @@ Once you have a patch file, reference it from the project entry in
82125
patch: some-project.patch
83126
84127
From this point on, every ``dfetch update`` will fetch the upstream source and
85-
re-apply the patch on top.
128+
re-apply the patch on top. You can test the round-trip immediately:
129+
130+
.. code-block:: console
131+
132+
$ dfetch update --force some-project
133+
134+
The ``--force`` flag overwrites the working tree with the freshly fetched and
135+
patched version. Confirm the result looks right, then commit the manifest
136+
change and the patch file together.
137+
138+
**Organizing patch files**
139+
140+
Keep patch files alongside ``dfetch.yaml`` or in a dedicated subdirectory such
141+
as ``patches/``. *Dfetch* resolves patch paths relative to the manifest file,
142+
so as long as the path in the manifest matches the location on disk you have
143+
full flexibility. Committing the patch files to VCS ensures every team member
144+
and every CI run gets the same result.
86145

87146
**Multiple patches**
88147

@@ -96,8 +155,10 @@ order:
96155
- 002-add-missing-header.patch
97156
98157
Patches are applied in the order listed. A good convention is to prefix each
99-
file name with a zero-padded number so they sort correctly and their purpose is
100-
clear at a glance.
158+
file name with a three-digit, zero-padded number (``001-``, ``002-``, …) so
159+
they sort correctly and their purpose is clear at a glance. The ``dfetch update-patch``
160+
command always updates the **last** patch in the list, so the earlier patches represent
161+
stable, settled changes and the final one accumulates ongoing work.
101162

102163
See :ref:`patch` in the manifest reference for the full attribute syntax.
103164

@@ -106,22 +167,29 @@ See :ref:`patch` in the manifest reference for the full attribute syntax.
106167
Refreshing a patch
107168
------------------
108169

109-
As your local edits evolve — or when the upstream version changes — the
110-
existing patch file may no longer apply cleanly. Instead of manually
111-
regenerating it, run:
170+
As your local edits evolve, the existing patch file may become stale. Instead
171+
of manually regenerating it, run:
112172

113173
.. code-block:: console
114174
115175
$ dfetch update-patch some-project
116176
117-
This regenerates the last patch for ``some-project`` from the current working
118-
tree, keeping the upstream revision unchanged. It is safe to run repeatedly
119-
as you iterate on the fix.
177+
This command:
178+
179+
1. Re-fetches the upstream revision (without applying any patches).
180+
2. Computes the diff between that clean baseline and your current working tree.
181+
3. Overwrites the **last** patch in the manifest list with the new diff.
182+
4. Re-fetches the project and applies all patches so the working tree is left
183+
in the patched state.
184+
185+
It is safe to run repeatedly as you iterate on a fix. The upstream revision
186+
stays unchanged — only the patch file is updated.
187+
188+
.. note::
120189

121-
When the upstream version changes, *Dfetch* applies patches with fuzzy
122-
matching, so a patch can survive minor context changes without needing an
123-
immediate refresh. If the patch no longer applies at all, ``dfetch update``
124-
will report the failure and you can refresh with ``dfetch update-patch``.
190+
``update-patch`` requires the project directory to have **no uncommitted
191+
changes** in the superproject. Commit your work first (Git users can also
192+
``git stash``), then run the command.
125193

126194
.. asciinema:: ../asciicasts/update-patch.cast
127195

@@ -137,6 +205,77 @@ See :ref:`update-patch` in the command reference for all options.
137205

138206
.. scenario-include:: ../features/update-patch-in-svn.feature
139207

208+
.. _patching-upstream-bump:
209+
210+
Upgrading the upstream version
211+
-------------------------------
212+
213+
When you want to move to a new upstream release, update the ``tag``,
214+
``branch``, or ``revision`` in ``dfetch.yaml`` and then run ``dfetch update``.
215+
*Dfetch* fetches the new version and attempts to re-apply the patch using fuzzy
216+
matching, so patches often survive minor context changes automatically.
217+
218+
.. code-block:: console
219+
220+
$ # 1. Edit dfetch.yaml: change tag v1.2.3 → v1.3.0
221+
$ dfetch update some-project
222+
223+
Three outcomes are possible:
224+
225+
**Patch applies cleanly** — you are done. Review the result, commit the
226+
updated manifest and the updated vendored files.
227+
228+
**Patch applies with fuzz warnings** — the patch applied but the context lines
229+
shifted slightly. The files are in the correct state. Run
230+
``dfetch update-patch some-project`` to refresh the patch against the new
231+
baseline so it stays clean for future upgrades:
232+
233+
.. tabs::
234+
235+
.. tab:: Git
236+
237+
.. code-block:: console
238+
239+
$ git add some-project/
240+
$ git commit -m "vendor: update some-project to v1.3.0"
241+
$ dfetch update-patch some-project
242+
$ git add some-project.patch
243+
$ git commit -m "patches: refresh some-project.patch for v1.3.0"
244+
245+
.. tab:: SVN
246+
247+
.. code-block:: console
248+
249+
$ svn commit some-project/ -m "vendor: update some-project to v1.3.0"
250+
$ dfetch update-patch some-project
251+
$ svn commit some-project.patch -m "patches: refresh some-project.patch for v1.3.0"
252+
253+
**Patch fails to apply** — the upstream changes conflict with the local edits
254+
tracked in the patch. Resolve the conflict manually by editing the vendored
255+
files, then use ``dfetch update-patch`` to record the resolved state:
256+
257+
.. tabs::
258+
259+
.. tab:: Git
260+
261+
.. code-block:: console
262+
263+
$ # Manually resolve conflicts in some-project/
264+
$ git add some-project/
265+
$ git commit -m "vendor: update some-project to v1.3.0 with resolved conflicts"
266+
$ dfetch update-patch some-project
267+
$ git add some-project.patch
268+
$ git commit -m "patches: update some-project.patch for v1.3.0"
269+
270+
.. tab:: SVN
271+
272+
.. code-block:: console
273+
274+
$ # Manually resolve conflicts in some-project/
275+
$ svn commit some-project/ -m "vendor: update some-project to v1.3.0 with resolved conflicts"
276+
$ dfetch update-patch some-project
277+
$ svn commit some-project.patch -m "patches: update some-project.patch for v1.3.0"
278+
140279
.. _patching-upstream:
141280

142281
Contributing the patch upstream
@@ -152,10 +291,15 @@ for a project:
152291
$ dfetch format-patch some-project
153292
154293
This writes a ``formatted-some-project.patch`` file (or one file per patch if
155-
there are several) that is ready to share with the upstream project.
294+
there are several) in the current directory. Use ``--output-directory`` to
295+
place the formatted files in a specific location:
296+
297+
.. code-block:: console
156298
157-
Before sending it, do a dry-run check to confirm it applies cleanly to a local
158-
clone of the upstream repository:
299+
$ dfetch format-patch some-project --output-directory patches/upstream
300+
301+
Before sending a patch, do a dry-run check to confirm it applies cleanly to a
302+
local clone of the upstream repository:
159303

160304
.. tabs::
161305

@@ -188,3 +332,41 @@ clone of the upstream repository:
188332
.. asciinema:: ../asciicasts/format-patch.cast
189333

190334
See :ref:`format-patch` in the command reference for all options.
335+
336+
.. _patching-troubleshooting:
337+
338+
Troubleshooting
339+
---------------
340+
341+
**"No diffs found"**
342+
343+
``dfetch diff`` found no changes between the working tree and the upstream
344+
baseline recorded in ``.dfetch_data.yaml``. If you expected changes, make
345+
sure the edits are in the vendored directory and are not excluded by your
346+
VCS ignore rules. If the metadata file is missing, run
347+
``dfetch update some-project`` first to re-establish the baseline.
348+
349+
**Patch fails to apply after an upstream bump**
350+
351+
The upstream version introduced changes that conflict with the local edits
352+
in the patch. Follow the manual resolution workflow in
353+
:ref:`patching-upstream-bump`: edit the vendored files to the desired
354+
state, commit them, then run ``dfetch update-patch`` to regenerate the
355+
patch from the resolved working tree.
356+
357+
**"skipped - Uncommitted changes"**
358+
359+
``dfetch update-patch`` detected uncommitted changes in the project
360+
directory. Commit those changes first (Git users can also ``git stash``),
361+
then run the command so the patch calculation starts from a clean state.
362+
363+
**"skipped - the project was never fetched before"**
364+
365+
Run ``dfetch update some-project`` first. The project must exist on disk
366+
before a patch can be updated.
367+
368+
**"skipped - there is no patch file"**
369+
370+
The project has no ``patch:`` entry in the manifest. Use
371+
``dfetch diff some-project`` to create the initial patch, then add it to
372+
the manifest as described in :ref:`patching-wire`.

0 commit comments

Comments
 (0)