When doing a git rebase, I often have difficulty working out what is happening with the 'local' and 'remote' when resolving conflicts. I sometimes have the impression that they swap sides from one commit to the next.
This is probably (definitely) because I still haven't properly understood.
When rebasing, who is 'local' and who is 'remote'?
(I use P4Merge for resolving conflicts)
A merge commit is created to point to the latest local and remote commits. In case of a rebase, we use the command git pull --rebase . In a rebase, the unpublished local changes of the local branch are reapplied on top of the published changes of the remote repository.
No, locally rebasing doesn't change the remote.
Case 1: We should not do Rebase on branch that is public, i.e. if you are not alone working on that branch and branch exists locally as well as remotely rebasing is not a good choice on such branches and it can cause bubble commits.
The rebase operation itself combines resets, labels, merges to preserve the same structure. The tool itself rewinds before each merge tree, picks commits, then creates a merge commit. The very same steps can be done manually to achieve the same result.
To summarize (As Benubird comments), when:
git checkout A
git rebase B # rebase A on top of B
local
is B
(rebase onto), remote
is A
And:
git checkout A
git merge B # merge B into A
local
is A
(merge into), remote
is B
A rebase switches ours
(current branch before rebase starts) and theirs
(the branch on top of which you want to rebase).
kutschkem points out that, in a GUI mergetool context:
ours
" (the upstream branch) theirs
" - the current branch before the rebase.See illustrations in the last part of this answer.
The confusion might be related to the inversion of ours
and theirs
during a rebase.
(relevant extracts)
git rebase
man page:
Note that a rebase merge works by replaying each commit from the working branch on top of the
<upstream>
branch.
Because of this, when a merge conflict happens:
ours
' is the so-far rebased series, starting with <upstream>
,theirs
' is the working branch.
In other words, the sides are swapped.x--x--x--x--x(*) <- current branch B ('*'=HEAD)
\
\
\--y--y--y <- other branch to merge
, we don't change the current branch 'B', so what we have is still what we were working on (and we merge from another branch)
x--x--x--x--x---------o(*) MERGE, still on branch B
\ ^ /
\ ours /
\ /
--y--y--y--/
^
their
But on a rebase, we switch side because the first thing a rebase does is to checkout the upstream branch! (to replay the current commits on top of it)
x--x--x--x--x(*) <- current branch B
\
\
\--y--y--y <- upstream branch
A git rebase upstream
will first change HEAD
of B to the upstream branch HEAD
(hence the switch of 'ours' and 'theirs' compared to the previous "current" working branch.)
x--x--x--x--x <- former "current" branch, new "theirs"
\
\
\--y--y--y(*) <- upstream branch with B reset on it,
new "ours", to replay x's on it
, and then the rebase will replay 'their' commits on the new 'our' B branch:
x--x..x..x..x <- old "theirs" commits, now "ghosts", available through reflogs
\
\
\--y--y--y--x'--x'--x'(*) <- branch B with HEAD updated ("ours")
^
|
upstream branch
Note: the "upstream" notion is the referential set of data (a all repo or, like here, a branch, which can be a local branch) from which data are read or to which new data are added/created.
local
' and 'remote
' vs. 'mine
' and 'theirs
'Pandawood adds in the comments:
For me, the question still remains, which is "local" and who is "remote" (since the terms "ours" and "theirs" are not used when rebasing in git, referring to them just seems to make an answer more confusing).
kutschkem adds, and rightly so:
When resolving conflicts, git will say something like:
local: modified file and remote: modified file.
I am quite sure the question aims at the definition of local and remote at this point. At that point, it seems to me from my experience that:
ours
" (the upstream branch) theirs
" - the current branch before the rebase.git mergetool
does indeed mention 'local' and 'remote':
Merging:
f.txt
Normal merge conflict for 'f.txt':
{local}: modified file
{remote}: modified file
Hit return to start merge resolution tool (kdiff3):
For instance, KDiff3 would display the merge resolution like so:
And meld would display it too:
Same for VimDiff, which displays:
Invoke Vimdiff as a mergetool with git mergetool -t gvimdiff. Recent versions of Git invoke Vimdiff with the following window layout:
+--------------------------------+
| LOCAL | BASE | REMOTE |
+--------------------------------+
| MERGED |
+--------------------------------+
LOCAL
:
A temporary file containing the contents of the file on the current branch.BASE
:
A temporary file containing the common base for the merge.REMOTE
:
A temporary file containing the contents of the file to be merged.MERGED
:
The file containing the conflict markers.Git has performed as much automatic conflict resolution as possible and the state of this file is a combination of both
LOCAL
andREMOTE
with conflict markers surrounding anything that Git could not resolve itself.
Themergetool
should write the result of the resolution to this file.
The bottom line
git rebase
git merge
In other words, LOCAL is always the original, and REMOTE is always the guy whose commits weren't there before, because they're being merged in or rebased on top
Prove it!
Certainly. Don't take my word for it! Here's an easy experiment you can do to see for yourself.
First, make sure you have git mergetool configured properly. (If you didn't, you probably wouldn't be reading this question anyway.) Then find a directory to work in.
Set up your repository:
md LocalRemoteTest
cd LocalRemoteTest
Create an initial commit (with an empty file):
git init
notepad file.txt (use the text editor of your choice)
(save the file as an empty file)
git add -A
git commit -m "Initial commit."
Create a commit on a branch that isn't master:
git checkout -b notmaster
notepad file.txt
(add the text: notmaster)
(save and exit)
git commit -a -m "Add notmaster text."
Create a commit on the master branch:
git checkout master
notepad file.txt
(add the text: master)
(save and exit)
git commit -a -m "Add master text."
gitk --all
At this point your repository should look like this:
Now for the rebase test:
git checkout notmaster
git rebase master
(you'll get a conflict message)
git mergetool
LOCAL: master
REMOTE: notmaster
Now the merge test. Close your mergetool without saving any changes, and then cancel the rebase:
git rebase --abort
Then:
git checkout master
git merge notmaster
git mergetool
LOCAL: master
REMOTE: notmaster
git reset --hard (cancels the merge)
Your results should be the same as what's shown up top.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With