From: juejin. Cn/post / 684490…

preface

I started to use Git for code management since I got into programming. First, I played Github by myself, and then I used Gitlab in my work. Although I used it for a long time, I only used some common operations, such as pushing and pulling code, submitting and merging, etc., but I did not use more complicated operations, and gradually forgot the tutorials I had read. I’m sorry, Linus.

A few days ago, I encountered a very bad scenario in Git, and paid the price of a whole afternoon for not having a deep understanding of Git commands.

To give you an idea of this scenario, one project was upgraded from version N to version A by importing jar packages from another project, followed by release B and C

However, after the C version, I suddenly found that the JAR package introduced by the A version had A great performance problem. The B and C versions were released based on the A version, and it would take several days to repair the PERFORMANCE problem of the JAR package, but at this time there were urgent bugs to be repaired online, so I was in A dilemma.

I decided to roll back the code to pre-A and fix the bugs based on the old version, and it was five hours of misery.

Based on test

revert

Git revert Commit_id can be used to delete a commit

A commit with the exact opposite ID is called commit

Add to an ID, delete to a revert commit.

But after looking at the commit record using git log, I dismissed this idea because there were too many commits and several merge operations from other branches along the way.

To get from VERSION C to VERSION N, I have to do dozens of reverts in reverse order, and if I get one of them wrong, it might end up being the wrong thing.

In addition, we know that when we merge, the merge information also generates a new commit, and we need to specify the M parameter to revert this merge COMMIT to specify the mainline

The mainline is the main branch of the code we want to keep. The commit from the feature branch to the Develop branch or from the Develop branch to the Master branch is ok, but when the feature branches merge with each other, I don’t know which one is the main.

So the revert program was scrapped.

Reset

Reset also allows code to go back to a commit, but unlike Revert, reset points the submitted HEAD pointer to a commit and all subsequent commits disappear as if the commit had never happened.

But since we were both working on the feature branch, I got an error when I rolled back the code from the feature branch to a commit and merged it into the Develop branch.

This is because the Feature branch falls behind the Develop branch in git workflow after the feature branch is rolled back

Merge to the Develop branch, and you need to keep the latest sync with the Develop branch, merge the Develop branch into the feature branch, and then reset the code back.

Another option at this point is to reset on the master branch, discard the old code completely with the –hard option, and then force the reset to the remote end.

Git push --force origin masterCopy the code

But there are still some problems. First of all, our master branch is protected in GitLab and cannot use force push. After all, it is very risky.

In addition, reset is too barbaric, we still want to keep the submission history, later troubleshooting can also refer to.

To upgrade the fusion

rebase

You can use rebase to merge multiple submissions into a single submission and then use revert to make a single submission. This is a very clear way to combine the revert and Rebase commands. Equivalent to using an upgraded version that reverts.

Git Workflows are created with multiple commits. With rebase, you can change your commit history and change your commit information. Combine multiple Commits.

There is a lot of documentation about Rebase, so let’s go straight to using it for code rollback steps.

  1. First, cut out a new branch F and use git log to find the commit version N that you want to fall back to.
  2. Using the commandGit rebase -i N, -iAfter you specify the interaction mode, the Git rebase editing interface will open, which looks like:
Pick 6FA5869 COMMIT1 pick 0b84EE7 COMMIT2 Pick 986C6C8 COMMIT3 Pick 91A0DCC COMMIT4 Copy the codeCopy the code

  1. The commit is arranged from top to bottom, and we only need to add the action command before commit_id.

In merge COMMIT, select pick(p) the oldest COMMIT1, and add squash(s) to the commits of subsequent COMMIT_id to merge the commits to the oldest COMMIT1.

  1. After saving the rebase results and editing the commit information to invalidate the rebase, Git will delete all previous commits and merge their changes into a new COMMIT5

If an error occurs, you can also use git rebase –abort/–continue/–edit-todo to abort the previous edit and continue the edit.

  1. At this point, the commit record on the main branch is older, COMMIT1, COMMIT2, COMMIT3, COMMIT4

The commit record on F branch is older, COMMIT5. Since the ancestor node of F branch is OLDER, it is obviously behind commit4 of the main branch, merging F branch into the main branch is not allowed

Therefore, we need to execute git merge master to merge the primary branch with F branch. After merge, Git will find that the content submitted by COMMIT1 to COMMIT4 is exactly the same as the modification content of COMMIT5 on F branch, and the merge will be automatically performed. But there is an extra COMMIT5.

  1. Do a reverse revert on COMMIT5 on the F branch to rollback commit1 to COMMIT4.

The trick with this approach is that it takes advantage of rebase’s ability to handle historical commits and Git’s ability to recognize changes in the same way that it automatically merges.

Rebase is a very useful way to solve the problem of making git’s history messy by committing a small function multiple times, just avoid using it in branches that are being developed by multiple people.

Unfortunately, I did not understand the idea of Rebase that day, and I was too confused to try several methods but failed. After the completion of Rebase, I doubted the feasibility of these methods after the merger with the main branch was rejected. Moreover, some colleagues proposed a more feasible method, so I stopped the operation.

File operations

A more feasible approach is to manipulate the file and have Git recognize the changes:

  1. The main branch cuts off a branch F that is exactly the same as the main branch.
  2. Copy the project folder from the file management system to bak and use Git checkout N in bak to cut the code to the desired historical commit. Git will restore the file in Bak to the N state.
  3. On the slave file management system, copy and paste everything in the bak folder except the.git folder into the original project directory. Git identifies changes purely at the file level and then updates the workspace.
  4. Perform add and commit in the original project directory to complete the reverse commit.

The neat thing about this approach is that it takes advantage of Git’s own file recognition and doesn’t involve workflow operations.

summary

Finally, I successfully completed the code rollback with the help of file operation, which was a sad tears in hindsight.

To make my five hours worth, let’s go over the scenario and learn and summarize the four ways code can be rolled back:

  • Revert works well in situations where there are few historical commits that need to be rolled back and no merge conflicts.
  • Git reset –hard + git push –force
  • If you’re a bit geeky and want to revert code back and forth in a “proper and legitimate” way, Rebase + Revert is for you.
  • If you don’t care about elegance and want the simplest, most direct way, file manipulation is the way to go.

Git is really a great code management tool. It is easy to start with, three or five commands combined to do the job, and it is very friendly to Geekers. It supports all the operations you want.