← Documents Documentation/process/backporting.rst GitHub 원문 ↗

Linux 6.18.37 · 개발 절차

Kernel patch backport와 conflict 해결

Upstream fix를 이전 branch로 옮길 때 prerequisite를 찾고 diff3 conflict를 해결한 뒤 원본과 비교·build·runtime 검증하는 절차를 설명합니다.

Source pathDocumentation/process/backporting.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

먼저 clean base에 적용한 뒤 cherry-pick한다

backporting.rst:14-84

Mail patch를 old stable tree에서 곧바로 git am하면 context mismatch와 .rej 처리에 갇히기 쉽다. Patch가 cleanly 적용되는 original 또는 가까운 mainline base에 먼저 git am으로 commit을 만든 뒤 destination branch에 git cherry-pick하는 방식이 권장된다.

Git은 commit history를 알고 있어 code 이동과 줄 번호 변경을 추적하고 3-way conflict marker를 만든다. Raw patch의 fuzzy context matching보다 잘못된 위치에 조용히 적용될 위험이 낮다. b4 am --guess-base --prep-3way도 base 추정과 3-way 준비를 자동화할 수 있다.

git cherry-pick -x <upstream-commit>

# stable 제출용 changelog 표식
[ Upstream commit <upstream-commit> ]

-x는 원본 commit identity를 commit message에 남긴다. Stable submission은 subject 다음 첫 줄에 'commit <sha> upstream' 또는 '[ Upstream commit <sha> ]' 형식을 요구한다.

Conflict 원인 commit부터 찾는다

backporting.rst:86-152

Conflict는 patch가 기대한 변경 줄과 주변 context가 destination tree와 다를 때 발생한다. Backport source에는 있고 target에는 없는 refactoring이나 prerequisite가 일반적인 원인이지만, target에만 별도 backport가 있어 branch가 반대로 달라졌을 수도 있다.

Marker를 지우고 compile만 되게 만드는 것으로는 충분하지 않다. 차이를 만든 commit을 찾고 그 changelog를 읽어야 semantic dependency를 놓치지 않는다. 익숙하지 않은 subsystem에서는 이 이력이 conflict resolution의 설계 문서 역할을 한다.

필수 patch와 우연히 겹친 patch 구분

backporting.rst:223-245

Whitespace 정리나 symbol rename처럼 같은 줄을 건드렸지만 의미를 바꾸지 않은 patch는 incidental change이므로 target code에 맞춰 수동으로 흡수할 수 있다. 반면 fix가 수정하는 function 자체가 target에 없다면 그 function을 도입한 commit이 실제 prerequisite인지 검토해야 한다.

필수 prerequisite를 찾았으면 현재 작업을 git cherry-pick --abort로 중단하고 prerequisite부터 옮긴다. 이미 해결한 다른 file이 있다면 임시 copy로 보존할 수 있지만 최종 history는 dependency 순서가 명확해야 한다.

Combined diff, diff3와 zdiff3

backporting.rst:247-344

Default merge marker는 HEAD의 현재 tree와 picked commit 적용 후 내용을 두 부분으로 보여 준다. Conflict 중 plain git diff는 두 column marker를 쓰는 combined diff이며 ours→working tree와 ours→theirs 차이를 함께 표현한다.

git diff HEAD 또는 git diff --ours는 현재 branch와 resolution working tree만 비교해 일반 diff처럼 읽기 쉽다. 전체 branch 차이가 너무 많을 때 유용하다.

git config merge.conflictStyle diff3

<<<<<<< HEAD
현재 target code
||||||| parent of <commit>
upstream patch가 기대한 변경 전 code
=======
upstream patch 적용 후 code
>>>>>>> <commit>

Diff3는 target, upstream before, upstream after를 모두 보여 줘 original patch가 실제로 바꾼 의미를 구분할 수 있으므로 강하게 권장된다. Git 2.35의 zdiff3는 같은 세 영역에서 공통 줄을 잘라 marker를 더 작게 만든다.

Conflict resolution을 반복 검토하는 방법

backporting.rst:346-411

각 hunk마다 '왜 이 줄이 original patch에 들어갔는가'를 답한 뒤 target code에서 같은 목적을 구현한다. Unrelated context가 큰 conflict는 marker를 모두 제거하고 target version에 최소 semantic change를 손으로 다시 적용하는 편이 명확할 수 있다.

복잡한 conflict는 git add 또는 git add -i로 확인된 부분을 단계적으로 stage한다. git diff HEAD는 남은 working resolution을, git diff --cached는 지금까지 완성된 backport를 보여 준다.

File rename을 Git이 감지하지 못하면 작은 변경은 새 경로에 직접 옮긴다. 큰 변경은 rename threshold를 낮춰 시도하거나 target에서 임시 rename commit을 만들고 cherry-pick한 뒤 원래 이름으로 돌려 마지막에 squash하는 방법이 있다. Rename patch 자체를 backport하는 것은 보통 첫 선택이 아니다.

Function argument, error path와 refactoring

backporting.rst:413-471
  • 비슷해 보이는 i와 j 같은 argument가 바뀌지 않았는지 call site를 문자 단위로 확인한다.
  • 추가된 goto의 target label이 old branch에서도 같은 cleanup 순서를 의미하는지 확인한다.
  • Return, break와 continue도 target branch의 control-flow에서 같은 의미인지 확인한다.
  • git diff -W와 git show -W로 변경 function 전체를 표시해 hunk 밖 error path까지 검토한다.
  • Upstream에서 여러 위치의 공통 code가 helper로 합쳐졌다면 helper 한 곳의 fix를 old tree 여러 위치에 각각 적용해야 할 수 있다.
  • git grep으로 같은 bug pattern이 old branch의 다른 복제 code에도 남아 있는지 찾는다.

Backport 과정에서 upstream에도 같은 pattern이 여러 곳 남아 있음을 발견하면 original author에게 확인한다. 단순 conflict 해결이 새 bug 발견으로 이어지는 경우도 있다.

원본과 비교하고 build·runtime test한다

backporting.rst:473-550
colordiff -yw -W 200 \
  <(git diff -W <upstream-commit>^-) \
  <(git diff -W HEAD^-) | less -SR

make path/to/file.o

Side-by-side colordiff는 whitespace를 제외하고 original fix와 backport의 다른 줄을 강조한다. 특히 goto label이나 변경되지 않은 주변 context 차이를 눈에 띄게 만든다. Single object build는 빠른 compiler check지만 linker error를 찾지 못하므로 full build가 뒤따라야 한다.

Conflict 없이 compile되고 boot된다고 dependency가 완전한 것은 아니다. 서로 다른 hunk에서 register save와 use가 분리된 assembly 변경처럼 text conflict가 없지만 runtime state를 손상하는 경우가 있다. Final patch를 새 patch와 같은 수준으로 review하고 unit·regression test를 실행해야 한다.

Stable backport 제출 형식

backporting.rst:552-594
<original patch title>

[ Upstream commit <mainline rev> ]

<original changelog>
[ <conflict와 해결 방법 요약> ]
Signed-off-by: <name and email>

Subject prefix에는 PATCH 6.1.y처럼 대상 series를 표시한다. Active stable version마다 별도 patch를 보내고 각각 독립적으로 test한다. 결과에 대한 확신 수준을 솔직히 밝히고 관련 maintainer의 명시적 ack를 구한다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ===================================
4 Backporting and conflict resolution
5 ===================================
6
7 :Author: Vegard Nossum <[email protected]>
8
9 .. contents::
10 :local:
11 :depth: 3
12 :backlinks: none
13
14 Introduction
15 ============
16
17 Some developers may never really have to deal with backporting patches,
18 merging branches, or resolving conflicts in their day-to-day work, so
19 when a merge conflict does pop up, it can be daunting. Luckily,
20 resolving conflicts is a skill like any other, and there are many useful
21 techniques you can use to make the process smoother and increase your
22 confidence in the result.
23
24 This document aims to be a comprehensive, step-by-step guide to
25 backporting and conflict resolution.
26
27 Applying the patch to a tree
28 ============================
29
30 Sometimes the patch you are backporting already exists as a git commit,
31 in which case you just cherry-pick it directly using
32 ``git cherry-pick``. However, if the patch comes from an email, as it
33 often does for the Linux kernel, you will need to apply it to a tree
34 using ``git am``.
35
36 If you've ever used ``git am``, you probably already know that it is
37 quite picky about the patch applying perfectly to your source tree. In
38 fact, you've probably had nightmares about ``.rej`` files and trying to
39 edit the patch to make it apply.
40
41 It is strongly recommended to instead find an appropriate base version
42 where the patch applies cleanly and *then* cherry-pick it over to your
43 destination tree, as this will make git output conflict markers and let
44 you resolve conflicts with the help of git and any other conflict
45 resolution tools you might prefer to use. For example, if you want to
46 apply a patch that just arrived on LKML to an older stable kernel, you
47 can apply it to the most recent mainline kernel and then cherry-pick it
48 to your older stable branch.
49
50 It's generally better to use the exact same base as the one the patch
51 was generated from, but it doesn't really matter that much as long as it
52 applies cleanly and isn't too far from the original base. The only
53 problem with applying the patch to the "wrong" base is that it may pull
54 in more unrelated changes in the context of the diff when cherry-picking
55 it to the older branch.
56
57 A good reason to prefer ``git cherry-pick`` over ``git am`` is that git
58 knows the precise history of an existing commit, so it will know when
59 code has moved around and changed the line numbers; this in turn makes
60 it less likely to apply the patch to the wrong place (which can result
61 in silent mistakes or messy conflicts).
62
63 If you are using `b4`_. and you are applying the patch directly from an
64 email, you can use ``b4 am`` with the options ``-g``/``--guess-base``
65 and ``-3``/``--prep-3way`` to do some of this automatically (see the
66 `b4 presentation`_ for more information). However, the rest of this
67 article will assume that you are doing a plain ``git cherry-pick``.
68
69 .. _b4: https://people.kernel.org/monsieuricon/introducing-b4-and-patch-attestation
70 .. _b4 presentation: https://youtu.be/mF10hgVIx9o?t=2996
71
72 Once you have the patch in git, you can go ahead and cherry-pick it into
73 your source tree. Don't forget to cherry-pick with ``-x`` if you want a
74 written record of where the patch came from!
75
76 Note that if you are submitting a patch for stable, the format is
77 slightly different; the first line after the subject line needs to be
78 either::
79
80 commit <upstream commit> upstream
81
82 or::
83
84 [ Upstream commit <upstream commit> ]
85
86 Resolving conflicts
87 ===================
88
89 Uh-oh; the cherry-pick failed with a vaguely threatening message::
90
91 CONFLICT (content): Merge conflict
92
93 What to do now?
94
95 In general, conflicts appear when the context of the patch (i.e., the
96 lines being changed and/or the lines surrounding the changes) doesn't
97 match what's in the tree you are trying to apply the patch *to*.
98
99 For backports, what likely happened was that the branch you are
100 backporting from contains patches not in the branch you are backporting
101 to. However, the reverse is also possible. In any case, the result is a
102 conflict that needs to be resolved.
103
104 If your attempted cherry-pick fails with a conflict, git automatically
105 edits the files to include so-called conflict markers showing you where
106 the conflict is and how the two branches have diverged. Resolving the
107 conflict typically means editing the end result in such a way that it
108 takes into account these other commits.
109
110 Resolving the conflict can be done either by hand in a regular text
111 editor or using a dedicated conflict resolution tool.
112
113 Many people prefer to use their regular text editor and edit the
114 conflict directly, as it may be easier to understand what you're doing
115 and to control the final result. There are definitely pros and cons to
116 each method, and sometimes there's value in using both.
117
118 We will not cover using dedicated merge tools here beyond providing some
119 pointers to various tools that you could use:
120
121 - `Emacs Ediff mode <https://www.emacswiki.org/emacs/EdiffMode>`__
122 - `vimdiff/gvimdiff <https://linux.die.net/man/1/vimdiff>`__
123 - `KDiff3 <http://kdiff3.sourceforge.net/>`__
124 - `TortoiseMerge <https://tortoisesvn.net/TortoiseMerge.html>`__
125 - `Meld <https://meldmerge.org/help/>`__
126 - `P4Merge <https://www.perforce.com/products/helix-core-apps/merge-diff-tool-p4merge>`__
127 - `Beyond Compare <https://www.scootersoftware.com/>`__
128 - `IntelliJ <https://www.jetbrains.com/help/idea/resolve-conflicts.html>`__
129 - `VSCode <https://code.visualstudio.com/docs/editor/versioncontrol>`__
130
131 To configure git to work with these, see ``git mergetool --help`` or
132 the official `git-mergetool documentation`_.
133
134 .. _git-mergetool documentation: https://git-scm.com/docs/git-mergetool
135
136 Prerequisite patches
137 --------------------
138
139 Most conflicts happen because the branch you are backporting to is
140 missing some patches compared to the branch you are backporting *from*.
141 In the more general case (such as merging two independent branches),
142 development could have happened on either branch, or the branches have
143 simply diverged -- perhaps your older branch had some other backports
144 applied to it that themselves needed conflict resolutions, causing a
145 divergence.
146
147 It's important to always identify the commit or commits that caused the
148 conflict, as otherwise you cannot be confident in the correctness of
149 your resolution. As an added bonus, especially if the patch is in an
150 area you're not that familiar with, the changelogs of these commits will
151 often give you the context to understand the code and potential problems
152 or pitfalls with your conflict resolution.
153
154 git log
155 ~~~~~~~
156
157 A good first step is to look at ``git log`` for the file that has the
158 conflict -- this is usually sufficient when there aren't a lot of
159 patches to the file, but may get confusing if the file is big and
160 frequently patched. You should run ``git log`` on the range of commits
161 between your currently checked-out branch (``HEAD``) and the parent of
162 the patch you are picking (``<commit>``), i.e.::
163
164 git log HEAD..<commit>^ -- <path>
165
166 Even better, if you want to restrict this output to a single function
167 (because that's where the conflict appears), you can use the following
168 syntax::
169
170 git log -L:'\<function\>':<path> HEAD..<commit>^
171
172 .. note::
173 The ``\<`` and ``\>`` around the function name ensure that the
174 matches are anchored on a word boundary. This is important, as this
175 part is actually a regex and git only follows the first match, so
176 if you use ``-L:thread_stack:kernel/fork.c`` it may only give you
177 results for the function ``try_release_thread_stack_to_cache`` even
178 though there are many other functions in that file containing the
179 string ``thread_stack`` in their names.
180
181 Another useful option for ``git log`` is ``-G``, which allows you to
182 filter on certain strings appearing in the diffs of the commits you are
183 listing::
184
185 git log -G'regex' HEAD..<commit>^ -- <path>
186
187 This can also be a handy way to quickly find when something (e.g. a
188 function call or a variable) was changed, added, or removed. The search
189 string is a regular expression, which means you can potentially search
190 for more specific things like assignments to a specific struct member::
191
192 git log -G'\->index\>.*='
193
194 git blame
195 ~~~~~~~~~
196
197 Another way to find prerequisite commits (albeit only the most recent
198 one for a given conflict) is to run ``git blame``. In this case, you
199 need to run it against the parent commit of the patch you are
200 cherry-picking and the file where the conflict appeared, i.e.::
201
202 git blame <commit>^ -- <path>
203
204 This command also accepts the ``-L`` argument (for restricting the
205 output to a single function), but in this case you specify the filename
206 at the end of the command as usual::
207
208 git blame -L:'\<function\>' <commit>^ -- <path>
209
210 Navigate to the place where the conflict occurred. The first column of
211 the blame output is the commit ID of the patch that added a given line
212 of code.
213
214 It might be a good idea to ``git show`` these commits and see if they
215 look like they might be the source of the conflict. Sometimes there will
216 be more than one of these commits, either because multiple commits
217 changed different lines of the same conflict area *or* because multiple
218 subsequent patches changed the same line (or lines) multiple times. In
219 the latter case, you may have to run ``git blame`` again and specify the
220 older version of the file to look at in order to dig further back in
221 the history of the file.
222
223 Prerequisite vs. incidental patches
224 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
225
226 Having found the patch that caused the conflict, you need to determine
227 whether it is a prerequisite for the patch you are backporting or
228 whether it is just incidental and can be skipped. An incidental patch
229 would be one that touches the same code as the patch you are
230 backporting, but does not change the semantics of the code in any
231 material way. For example, a whitespace cleanup patch is completely
232 incidental -- likewise, a patch that simply renames a function or a
233 variable would be incidental as well. On the other hand, if the function
234 being changed does not even exist in your current branch then this would
235 not be incidental at all and you need to carefully consider whether the
236 patch adding the function should be cherry-picked first.
237
238 If you find that there is a necessary prerequisite patch, then you need
239 to stop and cherry-pick that instead. If you've already resolved some
240 conflicts in a different file and don't want to do it again, you can
241 create a temporary copy of that file.
242
243 To abort the current cherry-pick, go ahead and run
244 ``git cherry-pick --abort``, then restart the cherry-picking process
245 with the commit ID of the prerequisite patch instead.
246
247 Understanding conflict markers
248 ------------------------------
249
250 Combined diffs
251 ~~~~~~~~~~~~~~
252
253 Let's say you've decided against picking (or reverting) additional
254 patches and you just want to resolve the conflict. Git will have
255 inserted conflict markers into your file. Out of the box, this will look
256 something like::
257
258 <<<<<<< HEAD
259 this is what's in your current tree before cherry-picking
260 =======
261 this is what the patch wants it to be after cherry-picking
262 >>>>>>> <commit>... title
263
264 This is what you would see if you opened the file in your editor.
265 However, if you were to run ``git diff`` without any arguments, the
266 output would look something like this::
267
268 $ git diff
269 [...]
270 ++<<<<<<<< HEAD
271 +this is what's in your current tree before cherry-picking
272 ++========
273 + this is what the patch wants it to be after cherry-picking
274 ++>>>>>>>> <commit>... title
275
276 When you are resolving a conflict, the behavior of ``git diff`` differs
277 from its normal behavior. Notice the two columns of diff markers
278 instead of the usual one; this is a so-called "`combined diff`_", here
279 showing the 3-way diff (or diff-of-diffs) between
280
281 #. the current branch (before cherry-picking) and the current working
282 directory, and
283 #. the current branch (before cherry-picking) and the file as it looks
284 after the original patch has been applied.
285
286 .. _combined diff: https://git-scm.com/docs/diff-format#_combined_diff_format
287
288
289 Better diffs
290 ~~~~~~~~~~~~
291
292 3-way combined diffs include all the other changes that happened to the
293 file between your current branch and the branch you are cherry-picking
294 from. While this is useful for spotting other changes that you need to
295 take into account, this also makes the output of ``git diff`` somewhat
296 intimidating and difficult to read. You may instead prefer to run
297 ``git diff HEAD`` (or ``git diff --ours``) which shows only the diff
298 between the current branch before cherry-picking and the current working
299 directory. It looks like this::
300
301 $ git diff HEAD
302 [...]
303 +<<<<<<<< HEAD
304 this is what's in your current tree before cherry-picking
305 +========
306 +this is what the patch wants it to be after cherry-picking
307 +>>>>>>>> <commit>... title
308
309 As you can see, this reads just like any other diff and makes it clear
310 which lines are in the current branch and which lines are being added
311 because they are part of the merge conflict or the patch being
312 cherry-picked.
313
314 Merge styles and diff3
315 ~~~~~~~~~~~~~~~~~~~~~~
316
317 The default conflict marker style shown above is known as the ``merge``
318 style. There is also another style available, known as the ``diff3``
319 style, which looks like this::
320
321 <<<<<<< HEAD
322 this is what is in your current tree before cherry-picking
323 ||||||| parent of <commit> (title)
324 this is what the patch expected to find there
325 =======
326 this is what the patch wants it to be after being applied
327 >>>>>>> <commit> (title)
328
329 As you can see, this has 3 parts instead of 2, and includes what git
330 expected to find there but didn't. It is *highly recommended* to use
331 this conflict style as it makes it much clearer what the patch actually
332 changed; i.e., it allows you to compare the before-and-after versions
333 of the file for the commit you are cherry-picking. This allows you to
334 make better decisions about how to resolve the conflict.
335
336 To change conflict marker styles, you can use the following command::
337
338 git config merge.conflictStyle diff3
339
340 There is a third option, ``zdiff3``, introduced in `Git 2.35`_,
341 which has the same 3 sections as ``diff3``, but where common lines have
342 been trimmed off, making the conflict area smaller in some cases.
343
344 .. _Git 2.35: https://github.blog/2022-01-24-highlights-from-git-2-35/
345
346 Iterating on conflict resolutions
347 ---------------------------------
348
349 The first step in any conflict resolution process is to understand the
350 patch you are backporting. For the Linux kernel this is especially
351 important, since an incorrect change can lead to the whole system
352 crashing -- or worse, an undetected security vulnerability.
353
354 Understanding the patch can be easy or difficult depending on the patch
355 itself, the changelog, and your familiarity with the code being changed.
356 However, a good question for every change (or every hunk of the patch)
357 might be: "Why is this hunk in the patch?" The answers to these
358 questions will inform your conflict resolution.
359
360 Resolution process
361 ~~~~~~~~~~~~~~~~~~
362
363 Sometimes the easiest thing to do is to just remove all but the first
364 part of the conflict, leaving the file essentially unchanged, and apply
365 the changes by hand. Perhaps the patch is changing a function call
366 argument from ``0`` to ``1`` while a conflicting change added an
367 entirely new (and insignificant) parameter to the end of the parameter
368 list; in that case, it's easy enough to change the argument from ``0``
369 to ``1`` by hand and leave the rest of the arguments alone. This
370 technique of manually applying changes is mostly useful if the conflict
371 pulled in a lot of unrelated context that you don't really need to care
372 about.
373
374 For particularly nasty conflicts with many conflict markers, you can use
375 ``git add`` or ``git add -i`` to selectively stage your resolutions to
376 get them out of the way; this also lets you use ``git diff HEAD`` to
377 always see what remains to be resolved or ``git diff --cached`` to see
378 what your patch looks like so far.
379
380 Dealing with file renames
381 ~~~~~~~~~~~~~~~~~~~~~~~~~
382
383 One of the most annoying things that can happen while backporting a
384 patch is discovering that one of the files being patched has been
385 renamed, as that typically means git won't even put in conflict markers,
386 but will just throw up its hands and say (paraphrased): "Unmerged path!
387 You do the work..."
388
389 There are generally a few ways to deal with this. If the patch to the
390 renamed file is small, like a one-line change, the easiest thing is to
391 just go ahead and apply the change by hand and be done with it. On the
392 other hand, if the change is big or complicated, you definitely don't
393 want to do it by hand.
394
395 As a first pass, you can try something like this, which will lower the
396 rename detection threshold to 30% (by default, git uses 50%, meaning
397 that two files need to have at least 50% in common for it to consider
398 an add-delete pair to be a potential rename)::
399
400 git cherry-pick -strategy=recursive -Xrename-threshold=30
401
402 Sometimes the right thing to do will be to also backport the patch that
403 did the rename, but that's definitely not the most common case. Instead,
404 what you can do is to temporarily rename the file in the branch you're
405 backporting to (using ``git mv`` and committing the result), restart the
406 attempt to cherry-pick the patch, rename the file back (``git mv`` and
407 committing again), and finally squash the result using ``git rebase -i``
408 (see the `rebase tutorial`_) so it appears as a single commit when you
409 are done.
410
411 .. _rebase tutorial: https://medium.com/@slamflipstrom/a-beginners-guide-to-squashing-commits-with-git-rebase-8185cf6e62ec
412
413 Gotchas
414 -------
415
416 Function arguments
417 ~~~~~~~~~~~~~~~~~~
418
419 Pay attention to changing function arguments! It's easy to gloss over
420 details and think that two lines are the same but actually they differ
421 in some small detail like which variable was passed as an argument
422 (especially if the two variables are both a single character that look
423 the same, like i and j).
424
425 Error handling
426 ~~~~~~~~~~~~~~
427
428 If you cherry-pick a patch that includes a ``goto`` statement (typically
429 for error handling), it is absolutely imperative to double check that
430 the target label is still correct in the branch you are backporting to.
431 The same goes for added ``return``, ``break``, and ``continue``
432 statements.
433
434 Error handling is typically located at the bottom of the function, so it
435 may not be part of the conflict even though could have been changed by
436 other patches.
437
438 A good way to ensure that you review the error paths is to always use
439 ``git diff -W`` and ``git show -W`` (AKA ``--function-context``) when
440 inspecting your changes. For C code, this will show you the whole
441 function that's being changed in a patch. One of the things that often
442 go wrong during backports is that something else in the function changed
443 on either of the branches that you're backporting from or to. By
444 including the whole function in the diff you get more context and can
445 more easily spot problems that might otherwise go unnoticed.
446
447 Refactored code
448 ~~~~~~~~~~~~~~~
449
450 Something that happens quite often is that code gets refactored by
451 "factoring out" a common code sequence or pattern into a helper
452 function. When backporting patches to an area where such a refactoring
453 has taken place, you effectively need to do the reverse when
454 backporting: a patch to a single location may need to be applied to
455 multiple locations in the backported version. (One giveaway for this
456 scenario is that a function was renamed -- but that's not always the
457 case.)
458
459 To avoid incomplete backports, it's worth trying to figure out if the
460 patch fixes a bug that appears in more than one place. One way to do
461 this would be to use ``git grep``. (This is actually a good idea to do
462 in general, not just for backports.) If you do find that the same kind
463 of fix would apply to other places, it's also worth seeing if those
464 places exist upstream -- if they don't, it's likely the patch may need
465 to be adjusted. ``git log`` is your friend to figure out what happened
466 to these areas as ``git blame`` won't show you code that has been
467 removed.
468
469 If you do find other instances of the same pattern in the upstream tree
470 and you're not sure whether it's also a bug, it may be worth asking the
471 patch author. It's not uncommon to find new bugs during backporting!
472
473 Verifying the result
474 ====================
475
476 colordiff
477 ---------
478
479 Having committed a conflict-free new patch, you can now compare your
480 patch to the original patch. It is highly recommended that you use a
481 tool such as `colordiff`_ that can show two files side by side and color
482 them according to the changes between them::
483
484 colordiff -yw -W 200 <(git diff -W <upstream commit>^-) <(git diff -W HEAD^-) | less -SR
485
486 .. _colordiff: https://www.colordiff.org/
487
488 Here, ``-y`` means to do a side-by-side comparison; ``-w`` ignores
489 whitespace, and ``-W 200`` sets the width of the output (as otherwise it
490 will use 130 by default, which is often a bit too little).
491
492 The ``rev^-`` syntax is a handy shorthand for ``rev^..rev``, essentially
493 giving you just the diff for that single commit; also see
494 the official `git rev-parse documentation`_.
495
496 .. _git rev-parse documentation: https://git-scm.com/docs/git-rev-parse#_other_rev_parent_shorthand_notations
497
498 Again, note the inclusion of ``-W`` for ``git diff``; this ensures that
499 you will see the full function for any function that has changed.
500
501 One incredibly important thing that colordiff does is to highlight lines
502 that are different. For example, if an error-handling ``goto`` has
503 changed labels between the original and backported patch, colordiff will
504 show these side-by-side but highlighted in a different color. Thus, it
505 is easy to see that the two ``goto`` statements are jumping to different
506 labels. Likewise, lines that were not modified by either patch but
507 differ in the context will also be highlighted and thus stand out during
508 a manual inspection.
509
510 Of course, this is just a visual inspection; the real test is building
511 and running the patched kernel (or program).
512
513 Build testing
514 -------------
515
516 We won't cover runtime testing here, but it can be a good idea to build
517 just the files touched by the patch as a quick sanity check. For the
518 Linux kernel you can build single files like this, assuming you have the
519 ``.config`` and build environment set up correctly::
520
521 make path/to/file.o
522
523 Note that this won't discover linker errors, so you should still do a
524 full build after verifying that the single file compiles. By compiling
525 the single file first you can avoid having to wait for a full build *in
526 case* there are compiler errors in any of the files you've changed.
527
528 Runtime testing
529 ---------------
530
531 Even a successful build or boot test is not necessarily enough to rule
532 out a missing dependency somewhere. Even though the chances are small,
533 there could be code changes where two independent changes to the same
534 file result in no conflicts, no compile-time errors, and runtime errors
535 only in exceptional cases.
536
537 One concrete example of this was a pair of patches to the system call
538 entry code where the first patch saved/restored a register and a later
539 patch made use of the same register somewhere in the middle of this
540 sequence. Since there was no overlap between the changes, one could
541 cherry-pick the second patch, have no conflicts, and believe that
542 everything was fine, when in fact the code was now scribbling over an
543 unsaved register.
544
545 Although the vast majority of errors will be caught during compilation
546 or by superficially exercising the code, the only way to *really* verify
547 a backport is to review the final patch with the same level of scrutiny
548 as you would (or should) give to any other patch. Having unit tests and
549 regression tests or other types of automatic testing can help increase
550 the confidence in the correctness of a backport.
551
552 Submitting backports to stable
553 ==============================
554
555 As the stable maintainers try to cherry-pick mainline fixes onto their
556 stable kernels, they may send out emails asking for backports when
557 encountering conflicts, see e.g.
558 <https://lore.kernel.org/stable/2023101528-jawed-shelving-071a@gregkh/>.
559 These emails typically include the exact steps you need to cherry-pick
560 the patch to the correct tree and submit the patch.
561
562 One thing to make sure is that your changelog conforms to the expected
563 format::
564
565 <original patch title>
566
567 [ Upstream commit <mainline rev> ]
568
569 <rest of the original changelog>
570 [ <summary of the conflicts and their resolutions> ]
571 Signed-off-by: <your name and email>
572
573 The "Upstream commit" line is sometimes slightly different depending on
574 the stable version. Older version used this format::
575
576 commit <mainline rev> upstream.
577
578 It is most common to indicate the kernel version the patch applies to
579 in the email subject line (using e.g.
580 ``git send-email --subject-prefix='PATCH 6.1.y'``), but you can also put
581 it in the Signed-off-by:-area or below the ``---`` line.
582
583 The stable maintainers expect separate submissions for each active
584 stable version, and each submission should also be tested separately.
585
586 A few final words of advice
587 ===========================
588
589 1) Approach the backporting process with humility.
590 2) Understand the patch you are backporting; this means reading both
591 the changelog and the code.
592 3) Be honest about your confidence in the result when submitting the
593 patch.
594 4) Ask relevant maintainers for explicit acks.
595
596 Examples
597 ========
598
599 The above shows roughly the idealized process of backporting a patch.
600 For a more concrete example, see this video tutorial where two patches
601 are backported from mainline to stable:
602 `Backporting Linux Kernel Patches`_.
603
604 .. _Backporting Linux Kernel Patches: https://youtu.be/sBR7R1V2FeA
605

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

Patch를 Git commit으로 만든 뒤 destination에 옮긴다

1-85

Daily work에서 patch backport, branch merge, conflict resolution을 거의 하지 않는 developer에게 merge conflict는 부담스러울 수 있다. 하지만 conflict resolution도 연습 가능한 기술이며 과정을 매끄럽게 하고 결과 신뢰도를 높이는 여러 technique이 있다. 이 문서는 backport와 conflict resolution을 단계별로 설명한다. Author는 Vegard Nossum이다.

Backport할 patch가 이미 Git commit이면 git cherry-pick을 직접 사용한다. Linux kernel처럼 email에서 온 patch라면 먼저 git am으로 tree에 적용해야 한다.

git am은 patch가 source tree에 완벽히 맞기를 요구한다. .rej file을 직접 고치는 방식보다 patch가 cleanly apply되는 적절한 base version을 찾아 먼저 적용한 뒤 destination tree로 cherry-pick하는 것을 강하게 권장한다. 그러면 Git이 conflict marker를 만들고 Git 및 merge tool의 도움을 받아 해결할 수 있다.

예를 들어 LKML에 막 도착한 patch를 오래된 stable kernel로 backport하려면 recent mainline kernel에 먼저 적용한 다음 stable branch로 cherry-pick한다. Patch가 생성된 정확한 base가 가장 좋지만 clean apply되고 original base와 너무 멀지 않으면 큰 문제는 없다. Wrong base를 쓰면 old branch로 cherry-pick할 때 diff context에 unrelated change가 더 들어올 수 있다.

git am보다 git cherry-pick이 유리한 이유는 Git이 existing commit의 정확한 history를 알기 때문이다. Code가 이동해 line number가 달라진 것도 추적하므로 patch를 잘못된 위치에 적용해 silent error나 복잡한 conflict를 만드는 가능성이 낮다.

Email에서 b4로 직접 적용한다면 b4 am의 -g/--guess-base와 -3/--prep-3way option이 일부 과정을 자동화한다. 이 문서의 나머지는 plain git cherry-pick을 전제로 한다.

b4 am --guess-base --prep-3way <message-id>
git cherry-pick -x <commit>

Patch를 Git에 넣은 뒤 destination source tree로 cherry-pick한다. Origin 기록을 commit message에 남기려면 -x를 잊지 않는다.

Stable 제출 형식은 조금 다르다. Subject 다음 첫 line에 다음 둘 중 하나를 넣는다.

commit <upstream commit> upstream

[ Upstream commit <upstream commit> ]

Conflict가 생기는 이유와 merge tool

86-134
CONFLICT (content): Merge conflict

Conflict는 patch context, 즉 바꿀 line이나 주변 line이 적용 대상 tree와 맞지 않을 때 생긴다. Backport source branch에는 있고 destination branch에는 없는 patch가 원인인 경우가 많지만 반대도 가능하다.

Cherry-pick이 conflict로 실패하면 Git은 file에 conflict marker를 넣어 위치와 두 branch의 divergence를 보여 준다. Resolution은 다른 commit의 영향을 고려한 final result를 편집하는 일이다.

Regular text editor에서 직접 해결하거나 전용 conflict resolution tool을 사용할 수 있다. 직접 편집은 동작을 이해하고 final result를 통제하기 쉬운 장점이 있으며 상황에 따라 두 방식을 함께 쓸 가치가 있다.

git mergetool --help

Conflict를 만든 prerequisite commit 찾기: git log

136-193

대부분 conflict는 source branch에 있는 일부 patch가 destination branch에 없어서 생긴다. 독립 branch merge처럼 일반적인 경우에는 양쪽에서 development가 진행됐거나 old branch에 다른 conflict resolution backport가 들어가 divergence가 생겼을 수 있다.

Resolution 정확성을 신뢰하려면 conflict를 만든 commit을 항상 식별해야 한다. 익숙하지 않은 code라면 해당 commit changelog가 code context와 resolution의 잠재 problem을 이해하게 해 준다.

첫 단계는 conflict file의 git log를 보는 것이다. File 변경이 적으면 충분하지만 크고 자주 바뀌는 file이면 복잡해질 수 있다. Current checked-out branch HEAD와 cherry-pick commit parent 사이 range를 조회한다.

git log HEAD..<commit>^ -- <path>

git log -L:'\<function\>':<path> HEAD..<commit>^

git log -G'regex' HEAD..<commit>^ -- <path>

git log -G'\->index\>.*='

-L은 conflict가 난 function 하나로 history를 제한한다. Function 이름 주위의 \<와 \>는 word boundary에 match시킨다. 이 부분은 regex이고 Git은 첫 match만 따라가므로 boundary 없이 thread_stack을 찾으면 실제 target 대신 try_release_thread_stack_to_cache history만 나올 수 있다.

-G는 listed commit diff에 특정 regex가 나타나는 경우만 filter한다. Function call이나 variable이 언제 변경·추가·삭제되었는지 빠르게 찾거나 특정 struct member assignment처럼 더 구체적인 pattern을 검색할 수 있다.

git blame과 prerequisite·incidental 판정

194-245

주어진 conflict에서 가장 recent prerequisite commit을 찾는 다른 방법은 git blame이다. Cherry-pick할 patch의 parent commit과 conflict file을 대상으로 실행한다.

git blame <commit>^ -- <path>

git blame -L:'\<function\>' <commit>^ -- <path>

Conflict 위치로 이동하면 blame output 첫 column이 각 line을 추가한 commit ID다. git show로 이 commit이 conflict source인지 검토한다. 같은 area의 다른 line을 여러 commit이 바꾸거나 같은 line을 여러 차례 바꿨다면 원인이 여러 개일 수 있다. 더 오래된 file version을 지정해 git blame을 반복하면 history를 더 거슬러 올라갈 수 있다.

Conflict 원인 patch를 찾았으면 backport prerequisite인지 단순 incidental인지 판단한다. Incidental patch는 같은 code를 만지지만 의미를 실질적으로 바꾸지 않는다. Whitespace cleanup, function·variable rename만 하는 patch가 예다.

반대로 target branch에 변경 대상 function 자체가 없다면 incidental이 아니다. Function을 추가한 patch를 먼저 cherry-pick해야 하는지 신중히 판단한다.

필수 prerequisite가 있으면 current cherry-pick을 중단하고 prerequisite부터 적용한다. 다른 file conflict를 이미 해결해 다시 하기 싫다면 그 file을 임시 copy해 둘 수 있다.

git cherry-pick --abort
git cherry-pick -x <prerequisite-commit>

Conflict marker와 combined diff 읽기

247-312

추가 patch를 pick하거나 revert하지 않고 conflict를 직접 해결하기로 했다면 file에는 기본 merge-style marker가 들어 있다.

<<<<<<< HEAD
this is what's in your current tree before cherry-picking
=======
this is what the patch wants it to be after cherry-picking
>>>>>>> <commit>... title

Editor에서 보는 형태다. Argument 없이 git diff를 실행하면 일반 diff marker 한 column이 아니라 두 column인 combined diff가 나온다.

$ git diff
[...]
++<<<<<<<< HEAD
 +this is what's in your current tree before cherry-picking
++========
+ this is what the patch wants it to be after cherry-picking
++>>>>>>>> <commit>... title

이 3-way diff 또는 diff-of-diffs는 current branch와 current working directory 차이, 그리고 current branch와 original patch 적용 후 file 사이 차이를 함께 보여 준다.

Combined diff는 source와 destination 사이의 다른 변경까지 보여 주어 고려할 내용을 찾는 데 유용하지만 읽기 어렵다. git diff HEAD 또는 git diff --ours는 cherry-pick 전 current branch와 current working directory 사이만 보여 준다.

$ git diff HEAD
[...]
+<<<<<<<< HEAD
 this is what's in your current tree before cherry-picking
+========
+this is what the patch wants it to be after cherry-picking
+>>>>>>>> <commit>... title

일반 diff처럼 읽을 수 있어 current branch line과 merge conflict 또는 cherry-picked patch 때문에 추가되는 line을 구분하기 쉽다.

diff3와 zdiff3 conflict style

314-344

기본 marker는 merge style이다. Diff3 style은 두 부분 대신 세 부분을 보여 준다.

<<<<<<< HEAD
this is what is in your current tree before cherry-picking
||||||| parent of <commit> (title)
this is what the patch expected to find there
=======
this is what the patch wants it to be after being applied
>>>>>>> <commit> (title)

Diff3에는 Git이 찾을 것으로 예상했지만 실제 destination에는 없던 original context가 들어간다. Cherry-pick commit의 before와 after를 직접 비교할 수 있어 patch가 실제로 바꾼 내용을 명확히 이해하고 더 나은 resolution을 결정하게 하므로 강하게 권장된다.

git config merge.conflictStyle diff3

Git 2.35에서 추가된 zdiff3도 같은 세 section을 제공하지만 공통 line을 잘라 conflict area를 더 작게 만든다.

Resolution 반복과 rename conflict 처리

346-411

Conflict resolution의 첫 단계는 backport patch를 이해하는 것이다. Kernel에서는 잘못된 변경이 system crash나 발견되지 않은 security vulnerability로 이어질 수 있어 특히 중요하다. 모든 change와 hunk에 왜 이 hunk가 필요한지 질문해야 답이 resolution을 안내한다.

때로는 conflict의 첫 부분만 남겨 file을 사실상 unchanged로 만든 뒤 변경을 수동 적용하는 것이 쉽다. 예를 들어 patch가 function argument 0을 1로 바꾸는데 다른 change가 argument list 끝에 중요하지 않은 parameter를 추가했다면 해당 argument만 손으로 바꾸고 나머지를 유지한다. Unrelated context가 많이 들어온 conflict에 유용하다.

Marker가 많은 어려운 conflict는 git add 또는 git add -i로 해결한 부분을 선택적으로 stage해 치운다. git diff HEAD로 남은 resolution을 보고 git diff --cached로 지금까지 완성한 patch를 볼 수 있다.

git add -i
git diff HEAD
git diff --cached

Patch 대상 file이 rename되면 Git이 marker도 넣지 않고 unmerged path로 남길 수 있다. One-line처럼 작은 변경은 수동 적용하는 것이 가장 쉽지만 크고 복잡한 변경은 그렇게 해서는 안 된다.

첫 시도로 rename detection threshold를 기본 50%에서 30%로 낮출 수 있다. 두 file content가 최소 threshold만큼 같아야 add-delete pair를 rename 후보로 본다.

git cherry-pick -strategy=recursive -Xrename-threshold=30

Rename patch 자체를 backport해야 하는 경우도 있지만 흔하지 않다. 대안은 destination branch에서 git mv로 잠시 rename하고 commit한 뒤 cherry-pick을 다시 시도하고, git mv로 original name을 복원해 다시 commit한 다음 git rebase -i로 결과를 하나의 commit으로 squash하는 것이다.

Function argument, error path와 refactoring 함정

413-471

Function argument 변경을 주의한다. 두 line이 같아 보여도 전달한 variable이 i인지 j인지처럼 작은 차이가 있을 수 있다.

Cherry-pick patch에 보통 error handling용 goto가 있으면 destination branch에서도 target label이 올바른지 반드시 재확인한다. 새 return, break, continue도 마찬가지다. Error handling은 보통 function 끝에 있어 다른 patch로 바뀌었더라도 conflict에 포함되지 않을 수 있다.

git diff -W와 git show -W, 즉 --function-context로 변경을 검토하면 C code의 entire function을 보여 준다. Source 또는 destination branch에서 function의 다른 부분이 바뀐 문제를 더 쉽게 발견할 수 있다.

git diff -W
git show -W <commit>

Common sequence를 helper function으로 factor out하는 refactoring이 있었다면 backport에서는 반대 작업이 필요할 수 있다. Upstream helper 한 곳을 고친 patch를 old version의 여러 call-site pattern에 각각 적용해야 한다. Function rename은 이런 상황의 단서일 수 있지만 항상 그런 것은 아니다.

Incomplete backport를 피하려면 bug pattern이 여러 곳에 있는지 git grep으로 찾는다. 같은 fix가 적용될 다른 위치가 old branch에는 있지만 upstream에는 없다면 patch 조정이 필요할 가능성이 높다. Removed code는 git blame에 나오지 않으므로 git log로 해당 영역 history를 조사한다.

Upstream tree에 같은 pattern이 더 있고 bug인지 확신할 수 없다면 original patch author에게 묻는다. Backport 과정에서 새 bug를 발견하는 일은 드물지 않다.

Original patch와 backport를 side-by-side 검증

473-511

Conflict-free patch를 commit한 뒤 original patch와 비교한다. 두 file을 side-by-side로 놓고 차이를 color로 표시하는 colordiff 같은 tool을 강하게 권장한다.

colordiff -yw -W 200 <(git diff -W <upstream commit>^-) \
  <(git diff -W HEAD^-) | less -SR

-y는 side-by-side, -w는 whitespace ignore, -W 200은 output width 200을 뜻한다. 기본 130은 보통 좁다. rev^-는 rev^..rev의 shorthand로 single commit diff만 선택한다. git diff의 -W는 변경 function 전체를 보여 준다.

Colordiff는 서로 다른 line을 강조한다. Original과 backport에서 error goto label이 다르면 나란히 다른 color로 보여 주고, 두 patch가 직접 수정하지 않았지만 context가 다른 line도 강조해 manual inspection에서 눈에 띄게 한다.

이는 visual inspection일 뿐이며 실제 test는 patched kernel 또는 program을 build하고 실행하는 것이다.

Build와 runtime 검증

513-550

Runtime test 자체는 이 문서 범위 밖이지만 빠른 sanity check로 patch가 만진 file만 build할 수 있다. .config와 build environment가 올바르게 설정되어 있다고 가정한다.

make path/to/file.o

Single-file build는 linker error를 찾지 못하므로 성공 뒤에도 full build를 해야 한다. 먼저 file 하나를 compile하면 changed file에 compiler error가 있을 때 full build를 기다리는 시간을 아낀다.

Build나 boot 성공만으로 missing dependency를 배제할 수 없다. 같은 file의 독립된 두 change가 conflict와 compile error 없이 합쳐지지만 exceptional runtime path에서만 error를 만들 수 있다.

실제 예로 syscall entry code의 첫 patch가 register를 save·restore하고 나중 patch가 그 sequence 중간에서 같은 register를 사용했다. Diff 영역이 겹치지 않아 두 번째 patch만 conflict 없이 cherry-pick되지만 결과 code는 save되지 않은 register를 덮어썼다.

대부분 error는 compile 또는 간단한 execution에서 잡히지만 backport를 정말 검증하려면 일반 patch와 같은 수준으로 final patch를 review해야 한다. Unit test, regression test와 기타 automated test가 correctness confidence를 높인다.

Stable tree에 backport 제출

552-585

Stable maintainer가 mainline fix를 stable kernel로 cherry-pick하다 conflict를 만나면 backport를 요청하는 email을 보낼 수 있다. 보통 correct tree에 patch를 cherry-pick하고 제출하는 정확한 step을 포함한다.

Changelog는 다음 expected format을 따라야 한다.

<original patch title>

[ Upstream commit <mainline rev> ]

<rest of the original changelog>
[ <summary of the conflicts and their resolutions> ]
Signed-off-by: <your name and email>

Older stable version은 Upstream commit line 대신 다음 형식을 사용했다.

commit <mainline rev> upstream.

Patch 적용 kernel version은 email subject에 표시하는 것이 가장 일반적이다. Signed-off-by 영역이나 --- 아래에 적을 수도 있다.

git send-email --subject-prefix='PATCH 6.1.y' <patch-files>

Stable maintainer는 active stable version마다 별도 submission을 기대하며 각각 따로 test해야 한다.

마지막 조언과 예제

586-604
  • 겸손한 태도로 backport에 접근한다.
  • Changelog와 code를 모두 읽어 backport 대상 patch를 이해한다.
  • 제출할 때 결과에 대한 confidence를 솔직하게 밝힌다.
  • 관련 maintainer에게 명시적 ack를 요청한다.

이 문서는 이상적인 backport process를 설명했다. Mainline patch 두 개를 stable로 실제 backport하는 구체적인 예는 Backporting Linux Kernel Patches video tutorial에서 볼 수 있다.