简体   繁体   中英

Git merge can wipe out changes, how to deal with it

During a merge commit creation. if any change is not staged and left outside the merge commit.(this do happen, and happens a lot when someone doesn't quite known what he's doing, just blindly type those mystery commands. or due to misclick on a GUI git tool.[they are non-technician, we tried to teach them, but failed. We let them use git so that they can update their files easily and keep up to date.]) Then the change is gone. the merge commit won't have a this change is undoed msg. it just doesn't appear in merge commit. the commit which made the change has the info about the change. but after merge the change is actually undoed without trace.

this is horrifying, we can't rely on merge commit info to tell what has been changed/undoed. In fact there's no easy way to tell.

One approach to solve this is to limit the ability to merge onto master to trustworthy maintainers.But as a fast developing mini group, this would add burden to the maintainer and slow us down.

I'm wondering is there anyone enforce merge commit to include info about changes that is actually reverted but not recorded?

Or can I ban the merge operation completely, only allow rebase to make sure all changes are recorded explicitly?

No automated source tool can be totally foolproof, as fools are too clever. :-)

I think that it is true that most people should rebase more often than merging (ie, I think git pull 's default action, which is to run git merge after running git fetch , is wrong). But this is no solution to the "people do merges incorrectly" problem, because rebasing is merging! Specifically, to "rebase" a commit, you copy the commit, as if with git cherry-pick . Usually this is small and simple and does not require any fancy merge work, but sometimes it goes wrong and you wind up with a full blown merge. So if people are going to get merges wrong, they may get their own rebases wrong too—and when you rebase multiple commits, each commit gets copied, so each one is a potential merge.

This means the only solution to this problem is to learn how to merge . I will have to leave that sort of instruction to others, but I will note that you can see what changes a merge brought in, with respect to the commit that was the tip of the branch before merging.

Suppose that you have two branches, eg, this series of commits:

...--o--*--A-----B---C   <-- master
         \
          D--E--F--G--H   <-- feature/X

When you use git merge feature/X to create a new merge commit M on master , you get a new commit with two parents:

...--o--*--A-----B---C--M   <-- master
         \             /
          D--E--F--G--H   <-- feature/X

This new commit has its own independent source tree, just like any commit.

In your question, you say:

this is horrifying, we can't rely on merge commit info to tell what has been changed/undoed. In fact there's no easy way to tell.

But there is! Commit M (whatever its actual hash ID is) has two parents. One of those two parents is "what was on the branch before", ie, is commit C . If you want to see what has been changed in going from commit C to commit M , just ask Git to show you that :

git diff <hash-id-of-C> <hash-id-of-M>

The output from this git diff is the same as the output from any git diff : it's a set of instructions that show you how to take what was committed as C , and modify it to make what was committed as M .

You can, if you want, also have Git produce for you a set of instructions to show you how to take what was committed as H , and modify it to make what was committed as M :

git diff <hash-id-of-H> <hash-id-of-M>

As with any git diff , the output is a set of instructions: "how to take what is in the first commit and change it to make what is in the second commit."

There is an easy way to see both of these

It's true that git log -p , which normally shows you a diff from "parent commit" to "child commit"—eg, a diff of A vs B , or B vs C —shows nothing when applied to a merge commit. However:

git show -m <hash-id-of-M>

runs two git diff commands, one for each parent, and shows you both . Think of -m as "show a merge" or even "split a merge (into two diffs)". This -m flag is available in git log as well:

git log -m -p

shows each commit, one at a time, as either a simple diff (if it's not a merge) or a pair of simple diffs (if it is a merge, with two parents).

(There are, of course, ways to get just one of these two as well, but this answer is long enough already.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM