Can you ever revert a commit other than the very last without creating a conflict?

邮差的信 提交于 2019-12-10 20:29:07

问题


Note: stackoverflow's bot thinks the question is subjective but it is very factual - I am not asking for opinions here!

Let's say that I have one branch only, and the history of my commits are: A - B - C - D

If I click on C in a GUI (like GitKraken or Git for Windows) and do "revert commit", I get a file conflict message.

Two questions:

  1. Is this because the revert "undoes" the changes made in C, and GIT is now stuck with B and D which modify the file in different, incompatible ways? is that the reason?

  2. If yes, does this mean that you can only revert the very last commit? Can you ever revert a commit other than the very last without creating a conflict?

UPDATE: editing the question after the very helpful clarifications from @RomainValeri. I think I was making too much confusion between revert and reset. In his example below, doing

git revert B

causes the branch to go from

A-B-C-D

to

A-B-C-D-E

If B was the only commit which changed file2.txt, and B didn't make any other changes to other files, then the new commit E created after the revert will retain all the changes made in C and D but NOT those made in B. Is this correct?

This is because, technically, reverting means cancelling out, undoing any changes of a commit - is this correct?

Also: let's say that I have only one file in my working directory. If B is the only commit which changes the function fun1() in my file, while all the other commits change other functions within the same file, then reverting B will most likely cause a conflict, because git thinks in terms of lines in a file, not in terms of functions within a file. Is this correct?

So let's say that B changes fun1(), C changes fun2() and D changes fun3(). I then realise that the changes made at B in fun1() are wrong and I need to undo them. If all these functions are in the same file, is there a way to undo the changes in B while retaining those in C and D?

If, instead, each of these 3 functions is in a separate file, then it is much simpler to undo the changes of one commit without affecting the others, right?

I imagine this is, of course, one of the many reasons why a single file should never be too large nor contain too many functions doing different things, right?


回答1:


Short answer : Yes. You could very well revert a given commit (even an old one) and have no conflict.


Why is it so?

git revert creates a new commit on top of the current tip of the tree, it does not delete the given commit, nor modifies it in any way.

You have a conflict when something is different in the merge-base between the commit you're creating with the revert and HEAD.


Try it out for yourself with this

Trivial example

Given the repo tree,

Folder1
  file1.txt
  file2.txt
Folder2
  file3.txt

and following history,

A---B---C---D << HEAD

where

A creates all files with their contents.
B modifies file2.txt
C modifies file1.txt and file3.txt
D further modifies file1.txt

then the command

git revert B

would not create any conflict, just a fresh new commit E with new changes for file2.txt.

A---B---C---D---E << HEAD

Contents of the new commit created by git revert (added after comments)

The changes in E are the exact reverse changes that B introduced.

With B like this :

$ git show B
commit f3ba29d98c0998297126d686d03d33794cbc0f73
Author: Pythonista <some.name@some.isp>
Date:   Sun Mar 24 20:41:49 2019 +0100

    Some change

diff --git a/javascript/functions.js b/javascript/functions.js
index 543c9cb..5f166d3 100644
--- a/javascript/functions.js
+++ b/javascript/functions.js
@@ -3710,5 +3710,6 @@ function dumpLists(isMixted) {
 // end of process

 createTrigger(window, 'load', START_EVERYTHING);
+doStuff(param);

You would have an E commit containing :

$ git show E
commit 2ff723970e81d64f3776c081a1f96242589774b6 (HEAD -> master)
Author: Pythonista <some.name@some.isp>
Date:   Sun Mar 24 20:42:33 2019 +0100

    Revert "Some change"

    This reverts commit f3ba29d98c0998297126d686d03d33794cbc0f73.

diff --git a/javascript/functions.js b/javascript/functions.js
index 5f166d3..543c9cb 100644
--- a/javascript/functions.js
+++ b/javascript/functions.js
@@ -3710,6 +3710,5 @@ function dumpLists(isMixted) {
 // end of process

 createTrigger(window, 'load', START_EVERYTHING);
-doStuff(param);

Mind the + or - in front of the last diff line, meaning "addition" (+) and "deletion" (-).


Finally, to say a word about the concerns at the end of your post (which I mostly missed, for some reason) :

I don't think you should base architecture choices for your application upon source control worries. Git is quite a great tool, and it's smart enough to prevent a big part of the frequent trouble we would inevitably have without. Go for the best way to group your code along its own logic, regardless of git.

And by the way, conflict detection happens at chunk level, not file. Meaning that the same file could have been modified by two sources without provoking a conflict if they happen in different non-contiguous parts. I couldn't exactly say where's the threshold, but it's irrelevant to the point.




回答2:


Almost all the history-making operations other than straight commit in Git wind up having a merge involved somewhere. Rebase, cherry-pick, revert, stash pop, merge of course, they all merge, just with merge bases chosen as needed. There's even a merge option on checkout that merges using your current checkout as the base, the new commit as a tip and your worktree/index as the other tip, and merges into the new worktree/index, that can be gratifyingly handy.

For a revert, say A---B... * ... M master and you git revert B, git looks at the changes from B to A and the changes from B to M, that's your merge. If adjacent or overlapping lines got touched in the two sets of changes, the odds some human needs to fix up the text skyrocket, so Git refuses to do it silently. The trivial cases take seconds anyway.



来源:https://stackoverflow.com/questions/55322462/can-you-ever-revert-a-commit-other-than-the-very-last-without-creating-a-conflic

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!