-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdevlog-patch-merge-default.html
More file actions
149 lines (130 loc) · 5.12 KB
/
Copy pathdevlog-patch-merge-default.html
File metadata and controls
149 lines (130 loc) · 5.12 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>XITLOG - devlog patch merge default</title>
<style>
html {
background-color: rgba(0, 50, 70, 1.0);
color: rgba(200, 200, 200, 1.0);
padding: 10px;
}
@font-face {
font-family: '3270';
src: url('term.ttf') format('truetype');
}
#grid {
font-family: '3270', monospace;
white-space: pre;
font-size: 22px;
line-height: 22px;
}
#grid a {
color: white;
}
</style>
</head>
<body>
<div id="grid"><a href='index.html'><- HOME</a> - a blog about <a href='https://github.com/xit-vcs/xit'>xit</a> <a href='feed.xml'>RSS</a>
██╗ ██╗██╗████████╗██╗ ██████╗ ██████╗
╚██╗██╔╝██║╚══██╔══╝██║ ██╔═══██╗██╔════╝
╚███╔╝ ██║ ██║ ██║ ██║ ██║██║ ███╗
██╔██╗ ██║ ██║ ██║ ██║ ██║██║ ██║
██╔╝ ██╗██║ ██║ ███████╗╚██████╔╝╚██████╔╝
╚═╝ ╚═╝╚═╝ ╚═╝ ╚══════╝ ╚═════╝ ╚═════╝
DEVLOG - PATCH-BASED MERGING IS NOW ENABLED BY DEFAULT -
December 17, 2025
Today I decided to finally <a href='https://github.com/xit-vcs/xit/commit/d4bdd53e4127d116c5504008a9a22021b6364851'>enable</a> patch-based merging by
default in xit. I believe xit is the first version control
system to have this feature while still being fully
git-compatible, and it is (for now) xit's marquee feature.
As I said in the <a href='https://github.com/xit-vcs/xit/blob/master/docs/patch.md'>documentation</a>, patch-based merging is
more reliable and leads to fewer conflicts than the
three-way merge that git uses. The example I used in that
doc was combining merging and cherry-picking, which git
fumbles so regularly that our entire industry has built
processes around avoiding it. We have collectively gaslit
ourselves into thinking it is inherently a bad idea, when
it really is just a limitation of git.
In addition to the cherry-picking problem, there is another
kind of merge conflict that git produces and xit avoids:
adjacent line conflicts. If one branch edits a line, and
another branch edits the line directly above or below it,
git will produce a conflict. People often <a href='https://softwareengineering.stackexchange.com/questions/194788/why-doesnt-git-merge-adjacent-lines-without-conflict'>wonder</a> why this
is a conflict -- they're different lines!
Imagine starting with this file:
1
2
3
4
On the master branch, you make this change:
1
2 changed on master
3
4
Then on the foo branch, you make this change:
1
2
3 changed on foo
4
What happens when you try to merge foo into master? With
git, you get this:
1
⟨⟨⟨⟨⟨⟨⟨ HEAD
2 changed on master
3
=======
2
3 changed on foo
⟩⟩⟩⟩⟩⟩⟩ foo
4
With xit, you get this:
1
2 changed on master
3 changed on foo
4
Many people, including in the above-mentioned stack
overflow post, assume that this is an intentional safety
feature in git, but that is nonsense. If we added even a
single empty line between 2 and 3 in the example above, git
would've auto-resolved the conflict! Why is it more
dangerous to merge changes that are next to each other
compared to those that are separated by one line?
The reality is that merging can *always* lead to invalid
code, because the VCS doesn't have any semantic
understanding of your code. If you add a new call to
`hello()` on branch A, and you delete the `hello` function
on branch B, guess what happens when you merge B into A? In
both git and xit, it completes successfully but the code is
broken. This is unavoidable.
The behavior above has nothing to do with safety, but
rather is a limitation of the diff3 algorithm that git uses
during a merge. When looking for where to place a change,
it uses the content of the parent line (the one immediately
above it) to decide where it should go. If that line was
changed on the other branch, this technique breaks,
resulting in a conflict.
The core problem is that git just doesn't have enough
information. All it can see is a line number and the
content, after all. In contrast, xit assigns a
globally-unique id to every line, allowing it to know
without ambiguity where it should go. This also explains
why git can <a href='https://tahoe-lafs.org/~zooko/badmerge/concrete-good-semantics.html'>sometimes</a> auto-resolve a conflict by putting
the change in the wrong place! While not as common as
adjacent line conflicts, it's scary that it's even possible.
Currently, the main downside of patch-based merging is that
it can take a long time to initially generate patches,
because it must do so for every commit in the history of
the repo. You can always run `xit patch off` if you want to
disable it, in which case xit will use the three-way merge
just like git. There are plenty of optimizations coming to
make patch generation faster, but you'll always have that
escape hatch if you need it.
</div>
</body>
</html>