Pregunta

I was linked to this blog on another question, and I read this warning about a certain git workflow:

Here's the actual scenario that caused an enormous amount of hair pulling.

  • The team was using the merge workflow. Lots of people changing things really fast. The typical style was
    • Work on your stuff
    • Commit it locally
    • git pull and hope for no conflicts
    • git push as fast as you can before somebody else gets in there
  • Many of the team members were using Tortoise Git, which works fine, but they had migrated from Tortoise SVN without understanding the underlying differences between Git and Subversion.
  • Merge conflicts happened fairly often because so many people were doing so many things
  • One user of Tortoise Git would do a pull, have a merge conflict, resolve the merge conflict, and then look carefully at his list of files to be committed back when he was committing the results. There were lots of files there, and he knew that the merge conflict only involved a couple of files. For his commit, he unchecked all the other files changes that he was not involved in, committed the results and pushed the commit.
  • The result: All the commits by other people that had been done between this user's previous commit and this one were discarded

I'm not sure I understand how this would happen? More specifically:

  • How are you able to push the 'wrong' state you're in? As I thought I understood it, this would require a --force, but the story here is that this is turned off. Wouldn't the user need a pull in between commiting the conflict-fixes and pushing?
  • Does discarded here even mean what I think it means? I'd have a hard time believing they are going to be 'removed', I suppose the actual result is more like that they are being undone?
¿Fue útil?

Solución

Yes, "discarded" here means being undone (there are in history, but topmost commit is without changes). I think that the result is similar to having pulled changes and choosing ours merge strategy, i.e. choosing always own version over changes.


Step by step explanation of what I think happened

When ready to publish

*---*---*---A---B          <--- user's master

*---*---*---X              <--- public repository master

"One user of Tortoise Git would do a pull", which is fetch...

*---*---*---A---B          <--- master
         \         
          \-X              <--- origin/master (remote tracking branch)

...followed by attempted merge: "have a merge conflict"...

*---*---*---A---B---[C]          <--- master
         \          / 
          \-X------/

Now there is a bit of ambiguity:

resolve the merge conflict, and then look carefully at his list of files to be committed back when he was committing the results. There were lots of files there, and he knew that the merge conflict only involved a couple of files. For his commit, he unchecked all the other files changes that he was not involved in, committed the results and pushed the commit.

My take on it is that "resolve... and then" should read "resolved... by", i.e. that said description is how user resolved merge, and not something done as separate step. Resolving merge ends with committing it:

*---*---*---A---B----B'          <--- user's master
         \          / 
          \-X------/

where B' is result of using ours-like merge strategy, where conflicted files were taken from user's version, replacing / forgetting changes in X

Assuming that there were no other activity in public repository, the 'master' branch in it still looks like this:

*---*---*---X              <--- public repository master

and push will be fast-forward and succeed:

*---*---*---A---B----B'     <--- public repository master
         \          / 
          \-X------/

When somebody else will pull new changes, he/she would see state B', which do not include changes from X because of mis-merge (incorrect manual conflict resolution).

Otros consejos

Jakub's answer is correct, but if someone wants a shorter explanation, here it is:

No commits were lost, nor removed from the remote repository. It's just that the merge commit reverted some code to a previous state.

The situation is similar to what happens when one uses git revert - imagine a history like this:

A---B---C---D

running git revert B would result in a new commit b that "undoes" the changes introduced by B. The history is now

A---B---C---D---b

Commit B is still there, but it's changes are removed by commit b.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top