loading...

git reflog saved my ass.

leanminmachine profile image leanminmachine ・2 min read

I’m a git noob.

I was panicking as usual because I accidentally committed my changes on to the master branch. We’re supposed to just git checkout and create a new branch, opening a pull request before merging to master.

So i was furiously googling, and this came up:

git reset —soft

git checkout - How to revert a Git repository to a previous commit - Stack Overflow
git reset --soft <commit>

  • Revert back while keeping the changes.
  • Does not change the working tree.
  • Goes back to commit status such as unstage files.

I tried using git reset --soft HEAD^ . I’m still not sure what’s the difference between HEAD^ and HEAD. Apparently:

Use ~ most of the time — to go back a number of generations, usually what you want
Use ^ on merge commits — because they have two or more (immediate) parents

Source: What’s the difference between HEAD^ and HEAD~ in Git? - Stack Overflow

So it worked, my changes got unstaged. But when i switched to a new branch, it seemed to have disappeared?? So i was panicking because usually i use git status and git log to check for modified files, staged / unstaged etc. I was so scared that all my code went missing.

git reflog

Thankfully, after googling, i found this command: git reflogRecovering a git reset —soft - Stack Overflow — which logs out commits that are not in my working tree anymore. Lo and behold, my commits are still there. Phew, all i have to do is to just reset back to that commit and make sure to push it up now.

Kinda weird why git log doesn’t show the commits.

Posted on by:

Discussion

markdown guide
 

i havent tried gitkraken, is it better than sourcetree? been using sourcetree but since a few mths back, i've switched to just terminal. now i just use git log --graph --oneline --decorate to see the structure haha

 

I added the following to my global git config, it's a pretty compact git flow IMO.

[alias]
    lg1=log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(dim white)- %an%C(reset)%C(bold yellow)%d%C(reset)' --all
    lg2=log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(bold yellow)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)' --all
    lg=!git lg1
 

I was Terminal-only for a while too. But for a simple GUI that speeds up your most common operations, I have to recommend GitKraken.

I still hang onto my terminal for when I need it though. GitKraken doesn't cover everything that, say, SourceTree does, but its GUI is much cleaner and I'm already comfortable enough in a terminal that I'm fine doing the more advanced stuff that way.

As an aside, I'd recommend being comfortable with git CLI anyway. Trying to learn with a GUI alone tends to hide a lot of the important stuff.

 

What left me wondering is why the master wasn't protected!?

Anyway, git-revert is super helpful in such incidences, it mutates the branch history though. But it makes sure you don't lose your changes under any incident. If you feel like you lost your changes, you can always go back to the reverted commit, checkout a branch from there and continue.

What i'd suggest in such scenario is,

  1. git branch newBranch: Create a new branch keeping the accedental commit, that'll preserve your development and the code so far.
  2. git reset --hard <commit hash> (on master). That'll leave the master unmutated.

Now switch to newBranch and continue with your business like nothing ever happened!

 

Wouldn't protecting a branch only affect the remote repo? I don't think she pushed to master.

 

HEAD always points to the commit that is checked out. HEAD~, which is the same as HEAD~1refers to HEADʼs parent. HEAD~2 is the parent of HEAD~1, and so on.

When you git chegkout a different commit, HEAD moves to that commit. When you git commit your changes, HEAD moves to the newly created commit.

When you do a git log <commitish> (where <commitish> can be a branch name, a tag, a commit hash, or pretty much anything then unambiguously refers to a commit, and defaults to HEAD if you donʼt specify it), Git will print that commit, its parent, the parent of that parent, and so on.

Contrast that to git reflog which displays the commit HEAD is pointing to, then the commit HEAD was pointing before (and can be referred to as HEAD@{1}), and so on.

Hope that helps, happy coding!

 

Whoa. Thanks for this. I also didn't realize they had that difference. But it does make sense that you'd need a way to specify which parent to choose, when more than one exist.

still not sure what’s the difference between HEAD^ and HEAD~

I'd summarize as: when a commit has more than one parent, ^ lets you say which one you want, ~ always returns the first.

What's confusing is that each can take a number, but treats it differently. <rev>^2 means "rev's second parent" and <rev>~2 means "rev's leftmost grandparent." It might help to think of merge commits as having left and right parents (^1 and ^2), but technically they can have more than 2.

Kinda weird why git log doesn’t show the commits.

My guess is the commits became unreachable. The commits are like a tree* where some of the leaves have markers (either tags or branches) pointing to them. Any commit that's pointed to by a marker or another commit is reachable. git log, despite the name, is just a list of the reachable commits.

Unreachable commits eventually get deleted. The reflog lists every commit, but being in the reflog doesn't prevent deletion.


* technically a DAG, because multiple parents are possible.

 

reflog can even help when you accidentally drop/clear stashed changes (those that weren't even committed really yet).

 

? how does it work if the changes arent committed or stashed somewhere..?

 

Well changes should be in the stash of course. Then you accidentally lose it (via git stash clear or git stash drop). However, even if changes weren't committed, they still do have some hash tags in the repository, they are stored as so called "dangled commits". So all you have to do is find such hash and apply it via stash command to restore. You can see an example here:

stackoverflow.com/questions/89332/...

nice, thanks for the link!

 

Try translate this page to understand reset, checkout, branch and so on. Very good article.

miximum.fr/blog/enfin-comprendre-git/

 

GUI's git application provide speedup over all activity but most of users makes mistakes on GUI interface. Guy who on terminal never make kind of mistakes.

 

This can save many from the panic attack. Thanks for posting this.