Skip to content

Commit 0493247

Browse files
dschoGit for Windows Build Agent
authored andcommitted
fixup! Add an AGENTS.md file to help with AI-assisted debugging/development
This adds an extensive section about resolving merge conflicts during rebases, which happens quite often in Git for Windows' day-to-day. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent 90b5eff commit 0493247

2 files changed

Lines changed: 305 additions & 0 deletions

File tree

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*.py text eol=lf diff=python
88
*.bat text eol=crlf
99
*.png binary
10+
/AGENTS.md conflict-marker-size=32
1011
CODE_OF_CONDUCT.md -whitespace
1112
/Documentation/**/*.adoc text eol=lf whitespace=trail,space,incomplete
1213
/command-list.txt text eol=lf

AGENTS.md

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,310 @@ git commit -sm "fixup! release: add Mac OSX installer build" path/to/file
415415

416416
## Rebasing Workflow
417417

418+
Rebases are the bread and butter of Git for Windows: topic branches are
419+
rebased every time upstream Git releases a new version. This section covers
420+
the workflow for managing downstream patches through repeated rebases.
421+
422+
### Merging-Rebases
423+
424+
Git for Windows uses "merging-rebases" to maintain downstream patches. Unlike
425+
a flat series of commits, the downstream changes are organized as topic
426+
branches merged together, preserving the logical grouping of related changes.
427+
428+
Each integration branch (`main`, `shears/next`, `shears/seen`) contains a
429+
marker commit with the message "Start the merging-rebase to \<version\>". This
430+
commit separates upstream history from downstream patches. Reference it with:
431+
432+
```bash
433+
# Find the marker commit
434+
git log --oneline --grep="Start the merging-rebase" -1
435+
436+
# Reference it using commit message search syntax
437+
origin/main^{/Start.the.merging-rebase}
438+
```
439+
440+
When working with merging-rebases:
441+
442+
- **Downstream patches start after the marker**: Use
443+
`origin/main^{/Start.the.merging-rebase}..origin/main` to see all
444+
downstream commits
445+
- **Topic branches are merged, not rebased flat**: Each logical feature or
446+
fix is a branch merged into the integration branch
447+
- **Merge commits are preserved**: The rebase recreates the merge structure
448+
on top of the new upstream base
449+
450+
To compare downstream patches before and after a rebase:
451+
452+
```bash
453+
# Compare the old and new downstream patch series
454+
git range-diff \
455+
old-base^{/Start.the.merging-rebase}..old-branch \
456+
new-base^{/Start.the.merging-rebase}..new-branch
457+
```
458+
459+
### Starting a Merging-Rebase
460+
461+
To rebase the downstream patches onto a new upstream version, create a marker
462+
commit and use it as the base for an interactive rebase:
463+
464+
```bash
465+
# Variables for the commit message
466+
tag=v2.53.0
467+
# The previous marker - this becomes the exclusion point for --onto
468+
previousMergeOid=$(git rev-parse origin/main^{/Start.the.merging-rebase})
469+
tagOid=$(git rev-parse "$tag")
470+
tipOid=$(git rev-parse origin/main)
471+
472+
# Create the marker commit with two parents: the tag and the current tip
473+
markerOid=$(git commit-tree "$tag^{tree}" -p "$tag" -p "$tipOid" -m "Start the merging-rebase to $tag
474+
475+
This commit starts the rebase of $previousMergeOid to $tagOid")
476+
477+
# Graft the marker to appear as if it has only the tag as parent
478+
git replace --graft "$markerOid" "$tag"
479+
480+
# Use the marker as the base for rebasing (only commits after previousMergeOid)
481+
git rebase -r --onto "$markerOid" "$previousMergeOid" origin/main
482+
483+
# After the rebase completes, delete the replace ref
484+
git replace -d "$markerOid"
485+
```
486+
487+
The marker commit is created with two parents: the upstream tag and the
488+
current branch tip. The `git replace --graft` makes Git see only the tag as
489+
parent during the rebase, allowing the downstream commits to be cleanly
490+
rebased onto the new upstream. After the rebase completes, the replace ref
491+
is deleted to clean up.
492+
493+
#### The shears/* Branches
494+
495+
Upstream Git has four integration branches: `seen`, `next`, `master`, and
496+
`maint`. Git for Windows maintains a corresponding `shears/*` branch for each
497+
(`shears/seen`, `shears/next`, `shears/master`, `shears/maint`) that
498+
continuously rebases Git for Windows' `main` onto the respective upstream
499+
branch.
500+
501+
These branches are updated incrementally rather than from scratch, avoiding
502+
re-resolution of merge conflicts. The update process leverages reachability:
503+
504+
1. **Integrate new downstream commits**: If `origin/main` has commits not yet
505+
in the shears branch, rebase them on top (using `-r` to preserve branch
506+
structure). Update the marker commit's message and second parent.
507+
508+
2. **Integrate new upstream commits**: If the upstream branch has commits not
509+
yet integrated, rebase onto the new upstream tip. Update the marker commit
510+
accordingly.
511+
512+
The marker commit's second parent always points to the current `origin/main`
513+
tip, making it trivial to identify what downstream commits are included.
514+
Similarly, the marker's first parent (the upstream base) shows exactly which
515+
upstream version is integrated.
516+
517+
### When to Skip a Patch
518+
519+
Use `git rebase --skip` when the patch is already in the new base:
520+
521+
- **Upstreamed**: The patch was accepted upstream and is now in `seen`
522+
- **Backported**: A fix we backported is now included in the upstream base
523+
- **Superseded**: HEAD already contains evolved code that includes this
524+
change
525+
526+
Signs to skip rather than resolve: HEAD has the functionality, the
527+
conflict would discard the patch entirely, or `git range-diff` shows
528+
the downstream and upstream patches are equivalent.
529+
530+
To find the corresponding upstream commit for a conflicting patch:
531+
532+
```bash
533+
git range-diff --left-only REBASE_HEAD^! REBASE_HEAD..
534+
```
535+
536+
### Resolving Merge Conflicts
537+
538+
When resolving merge conflicts during a rebase (especially when squashing
539+
fixups), the goal is to **apply the minimal surgical change** that the
540+
patch intended, not to reconstruct entire functions or add duplicate code.
541+
542+
#### 1. Understand What the Patch Wants
543+
544+
First, examine the patch being applied:
545+
546+
```bash
547+
git show REBASE_HEAD
548+
```
549+
550+
Look at the actual changes (lines starting with `-` and `+`):
551+
- What lines are being removed?
552+
- What lines are being added?
553+
- What is the context (function name, nearby code)?
554+
555+
**Key insight**: The patch shows the *intent*---a specific small change to
556+
make. Focus on this, not on the conflict markers' content.
557+
558+
**Code movement detection**: If the patch shows large changes, check with
559+
`--ignore-space-change`:
560+
561+
```bash
562+
git show <conflicted-commit> --ignore-space-change
563+
```
564+
565+
This reveals whether the commit is primarily **moving code** (lots of
566+
whitespace changes) or making **logic changes** (actual code modifications).
567+
When code was moved and re-indented, focus only on the non-whitespace
568+
changes when resolving the conflict.
569+
570+
#### 2. Understand Where the Code Is Now
571+
572+
The conflict occurred because the code moved or changed since the patch was
573+
created. Find where that code actually exists now:
574+
575+
```bash
576+
# If the patch was changing a specific pattern, find all occurrences
577+
git grep -n "pattern from patch"
578+
579+
# View the conflicted file around those locations
580+
```
581+
582+
**Common mistake**: Assuming the conflict markers show you what to do. They
583+
do not---they just show where Git got confused.
584+
585+
#### 3. Apply the Surgical Change
586+
587+
Make **only** the change the patch intended, but in the current location:
588+
589+
- If the patch adds `--abbrev=12` to a range-diff call, find where that
590+
range-diff call is NOW and add it there
591+
- If the patch changes a `.split()` pattern, find where that pattern is NOW
592+
and change it
593+
- Do not copy entire functions from the conflict markers
594+
- Do not create duplicates
595+
596+
#### 4. Remove ALL Conflict Markers
597+
598+
Conflict markers make the file invalid code:
599+
```
600+
<<<<<<< HEAD
601+
=======
602+
>>>>>>> commit-hash
603+
```
604+
605+
**All three types of markers must be completely removed.**
606+
607+
#### 5. Verify the Resolution
608+
609+
**Critical**: After staging your resolution, verify it matches the patch
610+
intent:
611+
612+
```bash
613+
# Compare your staged changes to the original patch
614+
git diff --cached
615+
git rebase --show-current-patch
616+
617+
# Or more directly, compare to REBASE_HEAD
618+
git diff --cached
619+
git show REBASE_HEAD
620+
621+
# For code that was moved/re-indented, ignore whitespace
622+
git diff --cached --ignore-space-change
623+
git show REBASE_HEAD --ignore-space-change
624+
```
625+
626+
**Verify, verify, verify**: The output of `git diff --cached` should
627+
correspond closely to the diff in `git show REBASE_HEAD`. The line numbers
628+
and context will differ (because code moved), but the actual changes (the
629+
`-` and `+` lines) should match the patch intent.
630+
631+
**After completing a rebase**, always verify the final result:
632+
633+
```bash
634+
# Compare tree before and after rebase
635+
git diff @{1}
636+
637+
# Shows what changed in each rebased commit
638+
git range-diff @{1}...
639+
```
640+
641+
If the rebase was onto the same base commit (e.g., squashing fixups), the
642+
`git diff @{1}` should be empty---this proves the rebase only reorganized
643+
commits without changing the end result. If the rebase was onto a new base
644+
commit (e.g., rebasing onto a new upstream release), the diff should match
645+
the difference between the old and new base commits, modulo any changes
646+
from upstreamed or backported patches. The `git range-diff @{1}...` shows
647+
the intended amendments (like adding `--abbrev=12`) were correctly applied
648+
to each commit.
649+
650+
### Conflict Resolution Red Flags
651+
652+
These indicate you are doing it wrong:
653+
654+
- Your diff adds hundreds of lines when the patch only changed 3
655+
- Conflict markers remain in the file
656+
- Functions appear twice in the file
657+
- You added `<<<<<<< HEAD` or `=======` to the staged changes
658+
- Syntax check fails after resolution
659+
660+
### Key Conflict Resolution Lessons
661+
662+
1. **Context changes, intent does not** - The patch's line numbers are
663+
wrong, but the change is right
664+
2. **Conflict markers lie** - They show you where Git got confused, not
665+
what you should do
666+
3. **One change at a time** - If the patch adds one line, your resolution
667+
should add one line
668+
4. **Verify, verify, verify** - `git diff --cached` should match
669+
`git show REBASE_HEAD` (modulo context)
670+
5. **Post-rebase verification** - `git diff @{1}` (empty) and
671+
`git range-diff @{1}...` (shows amendments)
672+
6. **Ignore whitespace for code moves** - Use `--ignore-space-change` to
673+
see the actual logic changes when code was moved and re-indented
674+
7. **When in doubt, look at the range-diff** - `git range-diff` shows if
675+
you matched the intent
676+
677+
### Useful Rebase Tools
678+
679+
- `git rebase --show-current-patch` - See what change is being applied
680+
- `git show REBASE_HEAD` - Alternative to above, works better with
681+
`--ignore-space-change`
682+
- `git show <commit> --ignore-space-change` - See only logic changes, not
683+
whitespace/indentation
684+
- `git grep -n "pattern"` - Find where code moved to
685+
- `git log -L <start>,<end>:<file> REBASE_HEAD..HEAD` - See how upstream
686+
modified a line range since the original patch; invaluable for
687+
understanding how conflicting lines changed
688+
- `git diff --cached` - After staging resolution, verify it matches
689+
REBASE_HEAD
690+
- `git diff @{1}` - After rebase, compare tree before/after
691+
- `git range-diff @{1}...` - After rebase, verify intended changes were made
692+
- `git range-diff A^! B^!` - Compare original patch to your resolution
693+
694+
### Leveraging Rerere
695+
696+
Git's "reuse recorded resolution" (`rerere`) feature automatically records
697+
how you resolve conflicts and replays those resolutions when the same
698+
conflict recurs. This is invaluable for repeated rebases where the same
699+
downstream patches conflict with similar upstream changes.
700+
701+
When you see `Staged 'file' using previous resolution`, Git has applied a
702+
previously recorded resolution. Always verify these auto-resolutions are
703+
still correct---upstream context may have changed enough that the old
704+
resolution no longer applies cleanly.
705+
706+
To enable rerere:
707+
```bash
708+
git config --global rerere.enabled true
709+
```
710+
711+
### Automation Tips
712+
713+
When running rebases in automated or scripted contexts, disable the pager
714+
to avoid hangs:
715+
716+
```bash
717+
GIT_PAGER=cat git range-diff ...
718+
# or
719+
git --no-pager log ...
720+
```
721+
418722
### Fixup Commits
419723

420724
Downstream patches sometimes require adjustment due to changes in the

0 commit comments

Comments
 (0)