On Thu, 26 Jan 2017 16:29:12 -0800 (PST) AD S <a...@radianweb.com.au> 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 git-users+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.