I am working on a project that has two branches: master
and feature
The feature
branch was created some time ago and has numerous
Your mental image is close, but let's make it exact:
...--o--*--A--B <-- master
\
C--D--...--Z <-- feature
This is what you have now: the name master
points to tip commit B
(with each single-letter or o
or *
representing a commit). Each commit points back (left-ish) to its previous commit. Two commits, A
, and B
, are only on master
. A bunch of commits are only on feature
, namely C
through Z
here. Commit *
and all earlier (further-left) commits are on both branches.
What git rebase
does is locate commit *
by working backwards from commit Z
and also from the tip of master
. It then knows that it needs to copy commits C-Z. The copies are to start right after master. If we use C'
to name the copy of C
, D'
for the copy of D
, and so on, the final graph will look like this:
C'-D'-...-Z' <-- feature
/
...--o--*--A--B <-- master
\
C--D--...--Z [abandoned]
As I think you have realized, each copy is made one commit at a time, by turning each commit (which is a complete snapshot) into a set-of-changes. To get what changed in C
, Git does:
git diff
Git then tries to apply this change to the snapshot in So, you resolve this and commit (well, and Git goes on to copy This time, there should be no You may, however, get bitten by white-space changes or similar. It's worth taking a close look at each I suspect a more likely problem: Aside from the mystery of why Git seems to be repeating the change, when it should have only other changes in You can indeed squash some or all feature commits together, by rebasing B
, but it doesn't apply easily, so Git tries doing a merge: it compares *
to B
and sees that it should change *
-vs-C
. That's the first merge conflict.
git rebase --continue
commits): C' <-- HEAD (rebase in progress)
/
...--o--*--A--B <-- master
\
C--D--...--Z <-- feature
D
, by diffing D
vs C
to get a patch.git show
-ing commit D
, which likewise compares it to C
, producing a diff.
git diff
output, and checking on things like end-of-line attributes if you are using those (CRLF conversions). End of line stuff often affects every line in every file, but you can get cases of single-line problems, depending on who uses what editor, for instance.git diff
is synchronizing on the wrong lines. It finds some trivial things that match up, such as lines that read }
, and uses those to decide that two files are back in sync and then picks out "wrong changes". If this is the case, the cure—if there is one—is to instruct Git to use a smarter diff. In particular, the patience
diff tries not to synchronize on trivial lines, but rather only on significant (i.e., non-recurring) lines. This may or may not actually help—running git diff --diff-algorithm=patience
(or git show
with the same argument) on your commits may tell you. Whether you can get rebase to use a different diff algorithm depends on your Git version.C
-vs-D
, one thing you can do to help out is to use git rerere to get Git to re-use a recorded resolution (hence the name). Basically, when Git hits a merge conflict, if rerere
is enabled, Git writes the conflicting parts into its database, and then when you git add
the resolved file, Git writes the resolution (paired with the original conflict). Then, the next time Git hits a merge conflict, it checks the saved rerere data: if the conflict is for the same patch, it pulls out the recorded resolution, and uses that without interacting with you.feature
atop commit *
(using interactive rebase). Since these patches won't conflict with themselves—at least as long as they are played back "in order"—that can let you reduce the number of commits that require resolving. Whether you should do this is up to you: in general, if you are rebasing, you get new replacement commits anyway, so you might as well make the new ones "as pretty as possible" for future debugging or other code exploration. Clearly, it would be better to have five "does something interesting, but pretty much stand-alone" commits instead of 50 "do fragment of one thing, do another fragment, fix previous fragment, do another fragment, OK that thing is done move to next thing, oops fix tiny error, ..." so those are very good candidates for squashing-together. But, it's usually better to have five "does one thing" commits than one "does five things" commits: it means that if there is a bug in one of the five, you can fix that without worrying about the other four.