1. Learn Git
    1. Learn Git with Bitbucket Cloud
      1. Create a Git repository
      2. Copy your Git repository and add files
      3. Pull changes from your Git repository on Bitbucket Cloud
      4. Use a Git branch to merge a file
    2. Learn about code review in Bitbucket Cloud
      1. Fork a teammate's repository
      2. Copy your fork and make a change to the repository
      3. Create a pull request
  2. Getting Started
    1. What is version control
      1. Benefits of version control
    2. What is Git
      1. Performance
      2. Security
      3. Flexibility
      4. Version control with Git
    3. Why Git for your organization
      1. Git for developers
      2. Git for marketing
      3. Git for product management
      4. Git for designers
      5. Git for customer support
      6. Git for human resources
      7. Git for anyone managing a budget
    4. Install Git
      1. Install Git on Mac OS X
      2. Install Git on Windows
      3. Install Git on Linux
    5. Setting up a repository
      1. git init
      2. git clone
      3. git config
    6. Saving changes
      1. git add
      2. git commit
      3. git stash
    7. Inspecting a repository
      1. git status
      2. git log
    8. Viewing old commits
      1. Undoing Changes
        1. git checkout
        2. git revert
        3. git reset
        4. git clean
      2. Rewriting history
        1. git commit --amend
        2. git rebase
        3. git rebase -i
        4. git reflog
    9. Collaborating
      1. Syncing
        1. git remote
        2. git fetch
        3. git pull
        4. git push
      2. Making a Pull Request
        1. How it works
        2. Example
        3. Where to go from here
      3. Using Branches
        1. git branch
        2. git checkout
        3. git merge
      4. Comparing Workflows
        1. Centralized Workflow
        2. Feature Branch Workflow
        3. Gitflow Workflow
        4. Forking Workflow
    10. Migrating to Git
      1. SVN to Git - prepping for the migration
        1. For administrators
        2. Basic Git commands
        3. Git Migration Tools
        4. For developers
      2. Migrate to Git from SVN
        1. Prepare
          1. Convert
            1. Synchronize
              1. Share
                1. Migrate
                2. Advanced Tips
                  1. Advanced Git Tutorials
                    1. Merging vs. Rebasing
                      1. Conceptual Overview
                      2. The Golden Rule of Rebasing
                      3. Workflow Walkthrough
                      4. Summary
                    2. Reset, Checkout, and Revert
                      1. Commit-level Operation
                      2. File-level Operations
                      3. Summary
                    3. Advanced Git log
                      1. Formatting Log Output
                      2. Filtering the Commit History
                      3. Summary
                    4. Git Hooks
                      1. Conceptual Overview
                      2. Local Hooks
                      3. Server-side Hooks
                      4. Summary
                    5. Refs and the Reflog
                      1. Hashes
                      2. Refs
                      3. Packed Refs
                      4. Special Refs
                      5. Refspecs
                      6. Relative Refs
                      7. The Reflog
                      8. Summary

                  Titanium Armor: Recovering From Various Disasters

                  Nicola PaolucciNicola Paolucci
                  Back to list

                  git is an advanced tool. It features a philosophy that is dear to my heart: to treat developers as smart and responsible folks. This means that a lot of power is at your fingertips. The power to also shoot yourself in the foot - arguably with a titanium vest on - but shoot yourself nonetheless.

                  The topic of this post is to confirm what my colleague Charles O'Farrell said very well in the ever popular "Why Git?" article some time ago:

                  […] Git is actually the safest of all the DVCS options. As we saw above, Git never actually lets you change anything, it just creates new objects.

                  […] Git actually keeps track of every change you make, storing them in the reflog. Because every commit is unique and immutable, all the reflog has to do is store a reference to them. This means you're safe from harm and your code is always preserved, but there are situations where you might need some conjuring to get it back.

                  Let me give you a few real world examples of how to recover from trouble, going from simple to advanced:

                  Table Of Contents

                  1. How To Undo A (Soft) Reset And Recover A Deleted File
                  2. If You Lose A Commit During An Interactive Rebase
                  3. How To Undo reset --hard If You Only Staged Your Changes

                  How To Undo A (Soft) Reset And Recover A Deleted File

                  If you delete a file with git rm and immediately after you git reset your working directory (which is called a soft reset), you will find yourself with a missing file and a dirty working directory like the following:

                  On branch master
                  Changes not staged for commit:
                  (use "git add/rm file..." to update what will be committed)
                  (use "git checkout -- file..." to discard changes in working directory)
                  deleted:    test.txt

                  There are a couple of simple ways to go about restoring the file. One is to use a git reset --hard which will recreate the missing files but it will delete any local modifications you might have in your working directory.

                  The second is to just re-check out the file yourself with git checkout test.txt.

                  In a slightly different scenario, if the file was removed in an earlier commit, you can recover it by noting down the exact commit where the file was deleted and use the reference to pick the commit immediately before: git checkout commit_id~ — test.txt

                  Where the ~ (tilde) sign means the one before this one.

                  If You Lose A Commit During An Interactive Rebase

                  Interactive rebase is one of the most useful tools in the git arsenal; It allows to edit, squash and delete commits interactively.

                  Personally, I love to clean and streamline commits before sharing them with others. It's nice when each commit is an understandable and logical chunk of work. In the heat of development, or in a local private branch, I might commit furiously for a bit, then when I am satisfied I spend time cleaning up the history.

                  But what if while in the interactive rebase session you accidentally remove a commit line without realizing it and save?

                  This situation is slightly annoying cause you have just removed a commit from your history!

                  If you created a throwaway branch before starting the rebase - which you should always do - you‘re fine and you can restart the process all over. But what if you didn’t?

                  No worries, git has got you covered. Nothing is ever lost. This is a good opportunity to use the [reflog][6, which is an automatic mechanism recording where the tip of all branches has been for the past 30 days.

                  Say at the start of the rebase I had 4 commits like the following:

                  $ git log
                  cfdf880 [2 seconds ago] (HEAD, master) wrote fifth change
                  ab446e6 [34 seconds ago] changed one line
                  6e1a130 [25 minutes ago] third change
                  6566977 [26 minutes ago] second change
                  5feeb33 [26 minutes ago] initial commit

                  I rebase the last 4 commits and accidentally remove one line:

                  $ git rebase -i HEAD~4

                  Editor opens on:

                  pick 6566977 second change
                  pick 6e1a130 third change
                  pick ab446e6 changed one line
                  pick cfdf880 wrote fifth change
                  Rebase 5feeb33..cfdf880 onto 5feeb33


                  • p, pick = use commit
                  • r, reword = use commit, but edit the commit message
                  • e, edit = use commit, but stop for amending
                  • s, squash = use commit, but meld into previous commit
                  • f, fixup = like “squash”, but discard this commit's log message
                  • x, exec = run command (the rest of the line) using shell

                  These lines can be re-ordered; they are executed from top to bottom.

                  If you remove a line here THAT COMMIT WILL BE LOST.

                  However, if you remove everything, the rebase will be aborted.

                  Note that empty commits are commented out

                  I removed one commit line (pick ab446e6 changed one line), I saved and got this:

                  Successfully rebased and updated refs/heads/master.

                  Now, if I check the list of commits I see I lost one:

                  $ git log
                  3dd6845 [6 minutes ago] (HEAD, master) wrote fifth change
                  6e1a130 [32 minutes ago] third change
                  6566977 [32 minutes ago] second change
                  5feeb33 [32 minutes ago] initial commit

                  But the reflog has our lost commit:

                  $ git reflog
                  3dd6845 HEAD@{0}: rebase -i (finish): returning to refs/heads/master
                  3dd6845 HEAD@{1}: rebase -i (pick): wrote fifth change
                  6e1a130 HEAD@{2}: checkout: moving from master to 6e1a130
                  cfdf880 HEAD@{3}: commit: wrote fifth change
                  ----}} ab446e6 HEAD@{4}: commit: changed one line

                  Here ab446e6 is the sha-1 for the commit we lost. Armed with this id we can just cherry-pick it back in the code:

                  $ git cherry-pick ab446e6
                  [master 032c6a6] changed one line
                  1 file changed, 1 insertion(+), 1 deletion(-)

                  And as you can see the commit is now back in our history:

                  $ git log
                  032c6a6 [12 minutes ago] (HEAD, master) changed one line
                  3dd6845 [12 minutes ago] wrote fifth change
                  6e1a130 [37 minutes ago] third change
                  6566977 [38 minutes ago] second change
                  5feeb33 [38 minutes ago] initial commit

                  How To Undo reset --hard If You Only Staged Your Changes

                  I'll save you the speech to commit frequently - whether in a private short lived branch or a shared one - to protect you from heaps of problems.

                  But let‘s say you didn’t commit this time and you find yourself in the following scenario: you‘ve done some work, haven’t committed it yet but have git added it to the staging area.

                  Then tragedy strikes: you type git reset --hard and immediately realize that you've zeroed your local changes(!!) and brought back (in it's entirety) the previous commit.

                  This is a tough situation to be in. How do you recover your work? Is it even possible? The answer is yes! The solution involves some low level searching and can be approached in a couple of ways.

                  Since git creates new objects in the .git/objects folder as soon as something is added to the staging area we can look there for the most recently created objects:

                  In OSX (and BSD systems) you can do:

                  find .git/objects/ -type f | xargs stat -f "%Sm %N" | sort | tail -5

                  On Linux(with GNU tools) this should work:

                  find .git/objects/ -type f -printf '%TY-%Tm-%Td %TT %p\n' | sort | tail -5

                  With tail -n you can cut to the last n results if your repository is very big:

                  The result should be something like:

                  Apr  2 12:26:33 2013 .git/objects//pack/pack-4cf657357973915f6fb6f90a41f69a88c0b08bb7.pack
                  Apr  2 12:29:56 2013 .git/objects//3d/d6845a69cd59dac0851e1ae0bd69dde950c46b
                  Apr  2 12:29:56 2013 .git/objects//68/c983f0cefb4bee2f753516ab6352ee2f2b7d29
                  Apr  2 12:35:23 2013 .git/objects//03/2c6a653cc00c302020293e020d8d68326b112e
                  Apr  2 15:34:55 2013 .git/objects//df/485610889e98ff773b4440a032ea2ede1338b9

                  In my case my sample file was lost exactly around 15:34 so I can inspect and retrieve it with:

                  git show df485610889e98ff773b4440a032ea2ede1338b9

                  Remember that the folder is part of the sha-1 reference, so remove the / slash to compute the correct id.

                  Note that git has specific low level command to explore dangling objects and to verify their connectivity: git fsck. For an alternative solution to this problem using fsck, check out this very cool answer on Stack Overflow.


                  There are many more examples and scenarios to cover on this topic so I might come back later with more interesting cases.

                  One thing to take away from this should be the feeling that it's very very hard to screw up and lose your data when using git. That's all for now!

                  Follow the awesome @AtlDevtools team or me @durdn for more Git rocking.