What is the difference between "git reset" vs "git rebase"?

Git

Git Problem Overview


I have been playing around with git (still very noob) and I wanted to know the difference between "reset" and "rebase". Is the one more powerful than the other?

Say I wanted to delete the 3 commits in bold from the history, which one will be better to use, or should I tag it and then delete it with git tag -d <tagname>?

>17a64df 2012-06-21 | Hello uses style.css (HEAD, origin/style, master),
a6792e4 2012-06-21 | Added css stylesheet
801e13e 2012-06-21 | Added README
5854339 2012-06-21 | Added index.html
0b1dd4c 2012-06-21 | Moved hello.html to lib
55649c3 2012-06-21 | Add an author/email comment
9b2f3ce 2012-06-21 | Added an author comment
cdb39b0 2012-06-21 | Commit p tags with text (v1.1)
b7b5fce 2012-06-21 | This reverts commit a6faf60631b5fbc6ee79b52a1bdac4c971b69ef8.
a6faf60 2012-06-21 | Revert "Oops, we didn't want this commit"
a006669 2012-06-21 | Oops, we didn't want this commit
262d1f7 2012-06-21 | Added HTML header (v1)
b1846e5 2012-06-21 | Added standard HTML page tags (v1-beta)
bf1131e 2012-06-21 | Added HI TAG
02b86d0 2012-06-21 | First Commit

Git Solutions


Solution 1 - Git

They are completely different. git-reset works with refs, on your working directory and the index, without touching any commit objects (or other objects). git-rebase on the other hand is used to rewrite previously made commit objects.

So if you want to rewrite the history, git-rebase is what you want. Note that you should never rewrite history that was pushed and was available to someone else, as rebasing rewrites the objects making them incompatible with the old objects, resulting in a mess for anyone else involved.

That being said, what you want to do is interactive rebasing. Invoke it using git rebase -i 262d1f7 and you should get a prompt looking like this:

pick 262d1f7 Added HTML header (v1)
pick a006669 Oops, we didn't want this commit
pick a6faf60 Revert "Oops, we didn't want this commit"
pick b7b5fce This reverts commit a6faf60631b5fbc6ee79b52a1bdac4c971b69ef8.
pick cdb39b0 Commit p tags with text (v1.1)
pick 9b2f3ce Added an author comment
pick 55649c3 Add an author/email comment
pick 0b1dd4c Moved hello.html to lib
pick 5854339 Added index.html
pick 801e13e Added README
pick a6792e4 Added css stylesheet
pick 17a64df Hello uses style.css (HEAD, origin/style, master),

There, simply delete the lines for the commits you want to remove, save and exit the editor and Git will rewrite your history. Again, don’t do this if you already pushed the changes. In general having such commits in the history is perfectly fine.

Solution 2 - Git

I hope this layman explanation is correct.

Both git reset and git rebase will affect your local branch. They will force your local branch to be in sync with a certain commit. The difference is that:

  1. "git reset --hard {commit-id}" will use a commit in local history.
  2. "git rebase origin/{branch-name}" will use the latest commit in the repo

Additional Info

When to use reset?

Let's say you finish your work and commit locally. Then your cat walk across the keyboard, and somehow you carelessly commit your cat's work. Use reset.

When to use rebase?

Let's say you refactor the name of a function/class and this change affect many files. When you commit and try to push to origin, you realized your colleague has make some important changes and already pushed it to origin. Let's say you think refactoring the name again (using an IDE) is easier than going through all the conflict files, you can choose to rebase which will erase your work and keep your colleague's one untouched.

Why is every answer/tutorial contains so much technical disclaimer?

Because both reset and rebase can delete local changes forever. So users will need to know how to employ strategies (e.g. create a backup branch) to keep their work.

Solution 3 - Git

While "git reset" is definitely not the tool to use to remove only 3 commits from the history, I find it quite useful in at least one scenario, which seems to be not quite documented enough and still relevant to the question being asked:

say you have an 'environment-X' branch, used to deploy to a specific environment, which should at all (most) times reflect the 'master' branch except for a few well defined changes. If the history of that branch gets garbled up because of incorrect git usage or other reasons, it might be a good option to rebase it on top of master and squash all the non-master commits it has into one.

In such cases, interactive rebasing might be hard, and result in a lot of conflicts which need to be manually fixed, one by one.

A simpler operation is to

  1. find the common ancestor commit: git merge-base master environment-X
  2. reset to it: git reset $commit (this will leave the files on disk unchanged)
  3. add and commit the modified files - as a bonus this gives you the chance of verifying that there are no unexpected changes
  4. force push

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionQ10View Question on Stackoverflow
Solution 1 - GitpokeView Answer on Stackoverflow
Solution 2 - GitSydneyView Answer on Stackoverflow
Solution 3 - GitgggeekView Answer on Stackoverflow