Staging

Exercise 01 Staging

docker run -it gitforpragmatists/01-basics-01-staging

In order to commit a set of changes, each change must first be added to the staging area. Since only staged changes will be included in the commit you can make multiple changes to your working copy and commit them separately.

git status

A field of green sheep marked with a plus and red sheep marked with a minus, some are in a fenced area marked staging and the rest are in the open field marked working directory

To start to work out what commands we need to issue to achieve our desired staging area we can run git status. In our current repository, executing git status shows us:

$ git status
On branch main
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   README.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   CakeRecipe.md
        modified:   README.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        NewFile.md

There’s a couple of things going on here, it’s easiest to work from the bottom:

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        NewFile.md

There’s a file called NewFile.md which git can see but is not yet version controlled. Git calls files that are version controlled “tracked”.

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   CakeRecipe.md
        modified:   README.md

There are two files, CakeRecipe.md and README.md which are tracked and have changes which are not yet staged.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   README.md

README.md which is the same file as above has a change which is staged.

git diff & git add

Now that we know the status of our files, we can use git diff to see what changes they would bring to the repository.

For the already staged change we must invoke git diff with the --cached “flag”.

$ git diff --cached README.md
diff --git a/README.md b/README.md
index 8cb9b0a..1d7eb0e 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
 Welcome to exercise 01, staging!
+
+I'm a staged change.

This shows the two lines already added to README.md. Which I think I’ll leave in the staging area for now.

I invoked git diff with a file name above but you can also invoke it without an argument to run it on the whole repository.

$ git diff --cached
diff --git a/README.md b/README.md
index 8cb9b0a..1d7eb0e 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
 Welcome to exercise 01, staging!
+
+I'm a staged change.

For changes not in the staging area we can use git diff without any flags.

$ git diff README.md
diff --git a/README.md b/README.md
index 1d7eb0e..62760d0 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,5 @@
 Welcome to exercise 01, staging!

 I'm a staged change.
+
+I'm an unstaged change.

This shows us just the changes to README.md which are not yet staged. I like this change so I think I’ll add it to the staging area.

$ git add README.md

$ git status
On branch main
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   README.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   CakeRecipe.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        NewFile.md

$ git diff --cached README.md
diff --git a/README.md b/README.md
index 8cb9b0a..62760d0 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,5 @@
 Welcome to exercise 01, staging!
+
+I'm a staged change.
+
+I'm an unstaged change.

We’ve successfully moved the second change in README.md into the staging area!

Let’s see what changes have been made to CakeRecipe.md

$ git diff CakeRecipe.md
diff --git a/CakeRecipe.md b/CakeRecipe.md
index bdb4025..492260a 100644
--- a/CakeRecipe.md
+++ b/CakeRecipe.md
@@ -2,7 +2,7 @@ Basic Cake Recipe
 ====

 1. 250g butter
-2. 250g caster sugar
-3. a pinch of salt
+2. 250g salt
+3. a pinch of caster sugar
 4. 3 eggs
 5. a drop of vanilla

I’ve swapped the quantities of salt and sugar, seems like that wouldn’t make a very nice cake so I don’t think we want to stage this change. We’ll come to fixing our working copy in a minute.

Ok what about NewFile.md the untracked one.

$ git diff NewFile.md
$

We don’t get any output here at all. If we cat it instead

$ cat NewFile.md
I'm a completely new file which hasn't been version controlled at the time of writing.

We can see that the file does have contents but git cannot show us the diff. That’s because the file is untracked. I like these changes, so I want to add them to the staging area. Once we add it to the staging area we will be able to see the staged changes using --cached as before.

$ git add NewFile.md
$ git status
On branch main
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   NewFile.md
        modified:   README.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   CakeRecipe.md

$ git diff --cached
diff --git a/NewFile.md b/NewFile.md
new file mode 100644
index 0000000..516d641
--- /dev/null
+++ b/NewFile.md
@@ -0,0 +1 @@
+I'm a completely new file which hasn't been version controlled at the time of writing.
diff --git a/README.md b/README.md
index 8cb9b0a..62760d0 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,5 @@
 Welcome to exercise 01, staging!
+
+I'm a staged change.
+
+I'm an unstaged change.

Call outs

On git add: * git add can be invoked with a path to add all the files under that path. You’ll commonly see git add . used to add the whole working directory. This isn’t incorrect but I’d encourage you to avoid doing it, at least in the short term. Staging files without being sure what changes are in them increases the chances you will commit a change you don’t really want.

git reset & git checkout

Looking at the staged changes, I like each of them in isolation but they’re not really related to each other. I’m inclined to split them into two sets of changes so that my intention behind each change is clearer to other people working in this repository.

I want to keep my NewFile.md as part of the staging area but I want to take the changes to README.md back to unstaged. To do this I can use git reset with the file name.

$ git status
On branch main
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   NewFile.md
        modified:   README.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   CakeRecipe.md

$ git reset README.md
Unstaged changes after reset:
M       CakeRecipe.md
M       README.md
$ git status
On branch main
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   NewFile.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   CakeRecipe.md
        modified:   README.md

The result is just what we’re looking for. We can keep the changes to README.md for a later commit.

We can also reset the entire staging area by invoking git reset without argument.

I don’t think we’re ever going to commit the changes to CakeRecipe.md since they wouldn’t make a good cake so let’s throw away those changes with git checkout.

$ git status
On branch main
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   NewFile.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   CakeRecipe.md
        modified:   README.md

$ git checkout CakeRecipe.md
$ git status
On branch main
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   NewFile.md

Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   README.md

$

Call outs

On git checkout: * this step is destructive, the changes to CakeRecipe.md are gone and cannot be retrieved. * git checkout will only throw away un-staged changes. If you’ve got some changes that are staged like README.md was at the start of this exercise then the staged changes are preserved.

Practice

Try creating some new files and changes to existing files, then practice reviewing, staging, unstaging and discarding changes with git diff, git add, git reset and git checkout.

The docker container has nano and vim installed as well as a built in script do-work which takes a file name to append or create a change for testing purposes.

$ do-work README.md
$ git diff README.md
diff --git a/README.md b/README.md
index 8cb9b0a..0d0fff4 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,6 @@
 Welcome to exercise 01, staging!
+
+I'm a staged change.
+
+I'm an unstaged change.
+Lorem ipsum

When you’re comfortable juggling stuff in and out of the staging area, move onto the next exercise.