gitgit-commitgit-detached-head

How to save changes when in detached-head state?


Working with Git, I had to go back to a specific commit. I made some changes and I now want to commit them. What is a proper way of doing this?

My project is now in detached-HEAD state. Will my changes be saved if I make a commit with

git commit

? Otherwise, what should I do to not lose my changes?


Solution

  • Disclaimer: git isn't complicated, it's just versatile. Don't be scared off just because I've rambled into a long answer :)

    You had:
    master: a-b-c-d-e-f

    and wanted to change c. You did:
    * git checkout c (avoid checking out commits in future. Move the branch head instead)
    * changed some files

    You are in:

    master: a-b-c-d-e-f
                 \uncommitted-work,detached
    

    If you want to re-apply d-e-f on top of your changed "c"

    (If you have pushed, people downstream will be have to recover from upstream rebase)

    1. git stash .
    2. git checkout master
    3. git stash pop (resolve conflicts)
    4. git stage .
    5. git commit -m "temporary name for g"
    6. (master: a-b-c-d-e-f-g)
    7. git rebase c -i ("re-apply my current branch on to point c, and let me manipulate the commits interactively", i.e, re-parent (rebase) d-e-f onto a new c)
    8. Follow guide to interactive rebase. You want to re-order g so it's after c, then change the rebase command from pick to fixup. dd to delete a line, P to place it, i to enter insert mode to type "fixup" then :wq to save and exit vim.
    9. (master: a-b-c'-d'-e'-f', where c' is the result of you merging g and c during the rebase. d-e-f have become d'-e'-f' as their ancestry has changed so they're not the "same" commits as far as git is concerned, but their contents remain the same)

    If you want to undo d-e-f (and rewrite history as if you never made them)

    (If you have pushed, people downstream will be have to recover from upstream rebase) :

    1. git stash .
    2. git checkout master
    3. (master: a-b-c-d-e-f, with stashed files originally based upon c)
    4. git reset --hard c (discard all files and commits on master since c)
    5. (master: a-b-c, with stashed files)
    6. git stash pop (resolve conflicts)
    7. (master: a-b-c-*)
    8. git stage .
    9. git commit -m "description of g"
    10. (master: a-b-c-g)

    If you want to undo d-e-f (but keep them in history)

    1. git stash
    2. git revert --no-commit d
    3. git revert --no-commit e
    4. git revert --no-commit f
    5. git push
    6. git stash pop (will be no conflicts)
    7. git stage .
    8. git commit -m "Undo d-e-f in order to fix..."
    9. git push

    If you have git push d-e-f, and you want to keep them separate:

    Sounds like your new changes are for a new branch off master. git branch <foo>.