Small note
I will often say the phrase upstream to refer to hello-git
. This is
because i am treating hello-git
similar to github
.
A Problem We All Face
You have made a change... but you need to pull in changes from a remote repo, what do you do?
commit changes then rebase or merge? (i would pick rebase)
The problems of merge and #1
If you create a commit with changes that are half baked just to pull in origin changes you will have your (potentially) broken commit, a merge commit, then your fixing commit. If you need to revert these changes, it will get more difficult (more on reverting later).
This is a good case for rebase
Your changes with rebase, will get put on top of all the incoming changes which allows your partial commit easier to work with. This style also makes squashing easier
But i would rather have my partial commit not committed yet
This is where stash comes in.
Worktrees
Often the superior approach and we will discuss them later
Stash
git stash
will take every change tracked by git (change to index + change
to work tree) and store that result, much like a commit, into the "stash."
To quote from the man page
Use git stash when you want to record the current state of the working directory and the index, but want to go back to a clean working directory. The command saves your local modifications away and reverts the working directory to match the HEAD commit.
Stash is a STACK of temporary changes
Operations
You can push your changes into the stack by using
git stash
Stashes, much like commits, can come with a message (-m "<your message>"
)
git stash -m "my lovely message here"
Stashes can be listed out:
git stash list
git stash show [--index <index>]
To pop the latest stash:
git stash pop
To pop a stash at an index:
git stash pop --index <index> # works well with git stash list
Remember
man git-stash
is your friend. if you forget how a command works, please
review the manual first! Its the authority of how to use the tool
Problem
use branch trunk
- create an upstream change. Commit a small change to hello-git
echo "upstream change" >> upstream.md
- add a small change to
remote-git
don't commit
echo "downstream change" >> README.md
- now that we have an active tracked change pull in the upstream change
What error do you get?
Solution
- create the origin changes
# make sure you are on branch trunk
cd /path/to/hello-git
echo "upstream change" >> upstream.md
git add upstream.md
git commit -m "upstream changes"
[trunk 6849c67] upstream changes
1 file changed, 1 insertion(+)
create mode 100644 upstream.md
- create the downstream changes but do not commit
# make sure you are on branch trunk
cd /path/to/remote-git
echo "downstream change" >> README.md
- validate that the changes are tracked by the worktree
➜ remote-git git:(trunk) git status
On branch trunk
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
- Try to pull in origin changes
➜ remote-git git:(trunk) git pull
error: cannot pull with rebase: You have unstaged changes.
error: please commit or stash them.
Error
error: cannot pull with rebase: You have unstaged changes.
error: please commit or stash them.
Problem
Now that we have produced the error and the answer is clear: use stash.
Lets play around with stash a bit more. To become more familiar perform the following:
- stash your changes
- view your stash list
- pop your stashed chages
- stash your changes but with a custom message
- create more changes and stash those so we have 2 in the list
- pull in the upstream's changes
Solution
This will be a long set of changes, but they are all pretty simple.
- stash your changes
➜ remote-git git:(trunk) git stash
Saved working directory and index state WIP on trunk: 42afc8d A remote change
➜ remote-git git:(trunk) git status
On branch trunk
nothing to commit, working tree clean
NOTE
If you have changes to non indexed files then they will not be added to the stash command. Careful not to lose them.
- view your stash list
➜ remote-git git:(trunk) git stash list
stash@{0}: WIP on trunk: 42afc8d A remote change
(END)
To view the current stashed change (the one in position 0) use show
diff --git a/README.md b/README.md
index 9f276a6..2ca5a19 100644
--- a/README.md
+++ b/README.md
@@ -2,3 +2,4 @@ A
D
E
remote-change
+downstream change
- pop your stashed chages
➜ remote-git git:(trunk) git stash pop
On branch trunk
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (e318e20fb946c2a78700611129d9ae040b4cc80c)
- stash your changes but with a custom message
➜ remote-git git:(trunk) git stash -m "my very nice change about..."
Saved working directory and index state On trunk: my very nice change about...
➜ remote-git git:(trunk) git stash list
stash@{0}: On trunk: my very nice change about...
- create more changes and stash those so we have 2 in the list
Note
Having named stashes can be useful if you come back a week later to a project and forgot what you have stashed / where you have put your changes
➜ remote-git git:(trunk) echo "some other change" >> README.md
➜ remote-git git:(trunk) git stash -m "other changes"
Saved working directory and index state On trunk: other changes
➜ remote-git git:(trunk) git stash list
stash@{0}: On trunk: other changes
stash@{1}: On trunk: my very nice change about...
- pull in the upstream's changes
➜ remote-git git:(trunk) git pull
Updating 42afc8d..6849c67
Fast-forward
upstream.md | 1 +
1 file changed, 1 insertion(+)
create mode 100644 upstream.md
Problem
Now we need to get back our original changes. Please pop the changes of our first stash. Remember the stash is a stack like data structure. Therefore your first change isn't the first item in the stash.
Solution
We need to use pop
and --index
to accomplish this.
➜ remote-git git:(trunk) git stash pop --index 1
On branch trunk
Your branch is up to date with 'origin/trunk'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{1} (c58f8ce5300e4207031438236c5732901666a43a)
➜ remote-git git:(trunk) git commit -m 'greatest changes'
[trunk 7282922] greatest changes
1 file changed, 1 insertion(+)
Stashing is quite powerful and allows you to be able to bring in upstream changes without losing your work or creating commits which can be annoying to deal with!
Worktrees
We will talk more about these, but generally this is my favorite way to work in a fast evolving codebase