On Thu, 26 Jan 2017 16:29:12 -0800 (PST)
AD S <[email protected]> wrote:
> I push a file to a remote repo and get a merge conflict error. I know
> my file is totally correct - there's no need to look through the
> code, I just want to overwrite the remote file with mine.
>
> I've tried:
>
>
> - git merge --strategy-option ours PATH/FILE
> - git checkout --ours PATH/FILE
> - git pull --ours PATH/FILE
>
> but none of these seem to work. It either throws an error or doesnt
> overwrite the remote file.
>
> What's the correct way of doing this?
First, some terminological alerts which might indicate you did not yet
fully absorbed how distibuted VC systems work:
* It's impossible to push _a file_ to a remote repo: a minimal chunk of
information you can share using a DVCS -- that is, push or fetch --
is a single commit.
* A commit in a DVCS always represents the whole state of the whole
project maintained in the repository.
* It's impossible to get a merge conflict when pushing in a DVCS: such
systems never attempt to merge anything with anything else when you
push; they merely try to *plant* what you sent to update a branch onto
the tip commit of that branch.
Now please think of this a little harder.
As you supposedly know (you should know this), each commit existing
in a repository has one or more parents. Commits explicitly refer
to their parent commits by their SHA-1 names (hashes).
Such lineages are usually rendered using arrows where parent commits
"point to" their child commits; so these arrows are oriented along the
natural progression of the history evolvement:
So if we draw
...->P->C->...
this means C is a commit which has commit P as its parent.
Now suppose you're trying to push to a branch named "master",
and it currently contains this line of commits:
...->D->E->F->G->H
and you're trying to push to that branch a line of history which
ends in the sequence of commits
...->D->E->X->Y->Z
The remote Git instance would take this history, compare it with
what's already there and notice that what you sent and what it has
have E as the last common point, and then they diverge.
Git can't merely "graft" your commits X->Y->Z onto H because the
line of history which would result does not exist anywhere in any
repository, and Git has no way to know whether such line of history
would even make sense for the project (for instance, your commit X
could have just deleted all the files in the project).
Hence such cases require manual resolution by a human.
There are three possibilities:
1) You can do a so-called "forced push" (if allowed by the remote
repository) which would just _throw away_ these commits F->G->H
and replace them with your X->Y->Z.
In general, this is a last-resort operation, which is only done
when you're absolutely positively sure what will the result be.
2) You can fetch the current state of the branch as seen on the remote
and _merge_ it into your own state.
In the indicated state, that would be:
/->F->G->H-\ # "origin/master"
| |
...->D->E->X->Y->Z->M # the resulting merge commit
^ "master" before the merge
You can then push your
...->F->G->H-\
|
...->X->Y->Z->M
to the remote "master", and since the "diamond" you'll be pushing
includes the F->G->H sequence currently at the tip of that branch,
Git will happily accept your history.
3) You can fetch the current state of the branch as seen on the remote
and _rebase_ your commits on top of that new state.
The result will be
...->D->E->F->G->H->X'->Y'->Z'
that is, all the commits in your rebased sequence will change
their hashes (and may be even actual contents of the files --
this depends on how the textual changes introduced by those
commits were applied) but otherwise the commits will be the same.
Again, you can safely push the resulting history because it, too,
will contain the sequence F->G->H currently at the tip at the
branch want to update with your push.
The first takeaway to do is that conflicts on push only even happen
because what you try to push would not "naturally promote" the branch
you're trying to update.
The second takeaway to do is that Git never merges remotely.
Merging is always a human's undertaking which happens in someone's local
repository.
OK, so how do you resolve a conflict which might occur when you do (2)
or (3) with taking "their" version of a file?
Well, there are indeed two options.
The first one is a _modifier_ to a default merge strategy called (which
is called "recursive"). To use it, you do something like
git fetch origin
git checkout master
git merge -s recursive -Xtheirs origin/master
As soon as Git detects a merge conflict for a file, it will simply take
the remote (being merged) version of the file and call it a day.
The second approach is to do merging without any modifiers and when Git
flags a conflict manually "accept" "their" version of the offending
file. This requires _two_ steps for each file:
git checkout --theirs -- path/to/the/file
git add path/to/the/file
Note that the first command merely replaces the contents of the file
at path/to/the/file in the work tree by the contents of the same
file of "their" version. The conflict is not resolved after this: you
need to stage the new contents to be recoded in the next commit, and
this is what `git add` does. (You can also use a simpler `git add -u`
which just adds whatever is changed in the work tree.)
After resolving merge conflicts, you have to record a new commit to
actually record your merge (with conflicts resolved) and re-attempt
your push.
Just in case, also note that the new push can actually result in
another rejection -- simply because someone could have managed to push
their changes first, -- and hence your merged state would again not
promote the remote branch cleanly. In such case, you'll have to
reattempt reconciling your work with the new remote state.
--
You received this message because you are subscribed to the Google Groups "Git
for human beings" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.