Initialising, staging, and committing

Introduction

This example shows how bricks are added to the stage (or “staging area”) and committed. It demonstrates basic use of Git’s init, add and commit commands.

Use the next (and previous) buttons to move through the steps, and investigate the different components at each step.
If you have a keyboard, ← and → keys also work.

Initialising the repo

(HEAD -> main) fatal: your current branch does not have any commits yet

git init

In the beginning, there is no house and no version control here. The init command tells Git to start tracking changes.

The changes that you’re about to make take place on a branch: the timeline of its history. Initialising a new repo creates that (empty) branch and, by default, calls it main.

The repo is empty: there are no commits, and no Lego bricks. The log is empty.

You don’t need to start with an empty project: you can introduce Git after you’ve already put some bricks down.

If you clone another repo (technically, that’s a remote repo), the local repo you get is already being tracked so you don’t need to init it.

Start building the house

(HEAD -> main) fatal: your current branch does not have any commits yet

Put down the base

Git doesn’t know about the green base yet. So putting it on the tabletop doesn’t change anything in version control.

There are no commits and the green base is untracked.

In this case, you did git init before adding any bricks. But if you had already started building the house and then initialised the repo, it would be the same: the repo would be empty, and all the bricks would be untracked.

Add the base to the staging area

(HEAD -> main) fatal: your current branch does not have any commits yet

git add base

Use git add to stage the untracked green base.

The stage is where you put any changes that you want to be included in the next commit. So adding the base tells Git to start tracking it, starting from the next commit (which hasn’t happened yet).

There are no commits, the green base is staged, and nothing is untracked.

It is possible to commit things directly, bypassing the stage. But when you’re learning Git it’s best to always use the stage. In these examples, changes are always staged before they are committed.

Commit the base place

(HEAD -> main) * 544d21f - initial commit

git commit -m "initial commit"

This commits the changes you’ve put into the staging area to history.

Git generates a unique ID for every commit: here it’s 544d21f.

You use the -m option to add a commit message explaining why you made this commit: “initial commit”.

Now the repo contains one commit (introducing the green base). There’s nothing untracked and the stage is empty. The log shows the history of the project.

Git maintains a pointer called HEAD which points at the end of the current branch. Now you have a commit in your repo, HEAD automatically points at it.

Let’s build some walls

(HEAD -> main) * 544d21f - initial commit

Adding yellow bricks

The new yellow bricks are not yet in version control.

The green base has been committed, the stage is empty, and the yellow bricks are untracked.

Stage the yellow bricks

(HEAD -> main) * 544d21f - initial commit

git add some-wall-bricks

This adds the yellow bricks to the stage.

Notice how this isn’t the whole wall: you can keep adding things to the stage until you’re ready to commit all those changes to the next event.

Now you’ve added the bricks Git knows about them, so they are no longer untracked.

The green base is committed, the yellow bricks are on the stage, and nothing is untracked.

Lay more bricks

(HEAD -> main) * 544d21f - initial commit

Building continues...

Laying down more bricks means you’re introducing more untracked items. This shows how you can build up changes on the stage before you commit them.

Because you haven’t committed anything since the initial commit, the history of the repo still only contains the addition of the green base.

The green base is committed, some yellow bricks are on the stage (because they were added), others are untracked.

Add the new bricks to the stage

(HEAD -> main) * 544d21f - initial commit

git add more-wall-bricks

By adding these bricks to the stage too, you’re increasing the complexity of the change that’s about to be committed.

You can add or remove changes to the stage as you go along. In this example, the changes are simple, but in complex projects it’s useful to be selective about which changes you stage. This lets you break big changes into separate commits even if you made them at the same time.

The green base is committed, all the yellow bricks are on the stage, nothing is untracked.

And even more bricks!

(HEAD -> main) * 544d21f - initial commit

You don’t need to stage everything you’ve done

Maybe there are too many bricks here: we only need to go four bricks high on the ground floor. But this is OK, because you can decide what to add to the stage and what to leave out.

The green base is committed, some yellow bricks are on the stage (because they were added), others are untracked.

Add more (but not all) bricks to the stage

(HEAD -> main) * 544d21f - initial commit

git add not-all-bricks

When you add things to the stage, you don’t need to add everything that’s changed.

In this case, you’ve gone too high with the top layer of bricks. But that’s not a problem: if you don’t add them to the stage, they won’t be included in the next commit.

This step demonstrates how you can work with your project as you go — inspect it, experiment with it, and run tests on it — adding and removing things to the stage as you progress.

The green base is committed, most yellow bricks are on the stage (because you have added them already), but others remain untracked.

This example is using Lego bricks, but typically you indicate changes by giving the filename of the file or files you’ve changed. If you’ve made more than one change to the same file, you do not need to add all those changes. The interactive (-i) option lets you select specific changes within the file: use git add -i.

Commit the walls

(HEAD -> main) * c730af3 - add walls * 544d21f - initial commit

git commit -m "add walls"

After all the build-up, commit the new walls. The yellow bricks that you added to the stage are committed to history; the others are not.

The new commit gets a unique ID (c730af3), and the message “add walls”.

The green base and the walls are committed, the stage is empty, and unused bricks remain untracked. There are two commits in the history, which appear in the log.

HEAD automatically moves to point at this new commit. It’s keeping track of the end of the current branch.

Discard surplus bricks

(HEAD -> main) * c730af3 - add walls * 544d21f - initial commit

Untracked bricks are not in version control

The bricks you didn’t use were untracked so there’s nothing to tell Git: simply delete them.

The green base and the walls are committed, the stage is empty, and no items are untracked. There are two commits in the history.

Introduce the first floor

(HEAD -> main) * c730af3 - add walls * 544d21f - initial commit

New pieces are untracked until you add them

The floor is also the ceiling (depends which storey you’re on). Git knows nothing about this: it’s untracked.

The green base and the walls are committed, the stage is empty, and the red floor is untracked. There are two commits in the history.

Stage the floor

(HEAD -> main) * c730af3 - add walls * 544d21f - initial commit

git add floor

The next commit will give the house a red floor.

The green base and the walls are committed, the stage contains the red floor, and there are no untracked items. There are two commits in the history.

Inspect the log to see the history: there are only two commits because the new floor hasn’t been committed yet... that’s in the next step.

Commit the red floor

(HEAD -> main) * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

git commit -m "add floor"

Since the floor was the only item on the stage, comitting adds it to the history. The new commit gets an ID (and the message “add floor”), and appears in the log.

Everything in the house so far is committed, the stage is empty, there are no untracked items, and the log shows three commits.

Git automatically moves HEAD to point to the new commit.

HEAD points to where the next commit will go. Automatically attaching HEAD to the end of the current branch is how Git ensures that your changes grow into a sequence of events.

Gables and frames

(HEAD -> main) * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

Introducing gables and window & door frames

Now add gables to the ends of the house, and colourful frames to the holes in the walls.

This step demonstrates that you can choose the scope of your change. Here you could add the gables as one change, and then add the door and window frames as another. In this example, they’re going in together.

The policy of isolating changes into their smallest functional parts and committing them separately is known as making “atomic commits”. Deciding what those functional parts are can be very subjective, which is an example of how version control is a skill, not just a tool.

The base, walls and floor are committed, the stage is empty, and the gables and frames are untracked. The log shows three commits.

Add gables and frames

(HEAD -> main) * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

git add gables-and-frames

Stage the new pieces so you can commit them.

The base, walls and floor are committed, the stage now has the gables and frames, and nothing is untracked. The log shows three commits.

Commit the gables and frames

(HEAD -> main) * 31e220a - add gables+frames * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

git commit -m "add gables+frames"

Commit the changes you staged: the gables and colourful frames.

Everything in the house is committed, the stage is empty and nothing is untracked. The log shows four commits.

Because you made a commit, HEAD automatically advances to point at it.

Change frame colour to red

(HEAD -> main) * 31e220a - add gables+frames * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

Changing existing bricks, not adding new ones

Sadly the house design needs red window and door frames. So you must change the colour of pieces that have already been committed.

Until now, you’ve been adding new bricks on each commit. But as a project develops it’s more common to make changes rather than adding new things, and untracked items become rarer.

Compare committed with everything to see the difference in your “working tree”.
On the command line, use git diff to see the difference.

The house with colourful frames is committed but the red frames have changed. The stage is empty and nothing is untracked. The log shows four commits.

Stage the change of frame colour

(HEAD -> main) * 31e220a - add gables+frames * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

git add window and door frames

The change of colour is staged so it will be included in the next commit.

What really goes into the staging area are changes (not bricks, or files). So when you use git add you are adding changes to the stage. Introducing new things is one kind of change (so is modifying them, or removing them). These changes are what you are staging or committing.

In this example, you’re simply changing the colour of a brick. But with physical Lego, you’d need to remove the colourful bricks and replace them with new ones.

The house with colourful frames is committed. The stage contains the change of frames to red. Nothing is untracked. The log shows four commits.

Commit change of colour

(HEAD -> main) * 64d5510 - paint frames red * 31e220a - add gables+frames * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

git commit -m "paint frames red"

The frames have turned red and the colourful frames are consigned to history.

Everything is committed. The stage is empty and nothing is untracked. The log shows five commits.

Build the roof

(HEAD -> main) * 64d5510 - paint frames red * 31e220a - add gables+frames * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

Adding roof tiles

The new roof tiles are not yet in version control.

Everything apart from the roof is committed. The stage is empty and the new roof is untracked. The log shows five commits.

Stage the roof

(HEAD -> main) * 64d5510 - paint frames red * 31e220a - add gables+frames * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

git add roof

As usual, you need to introduce the new items to version control by adding it to the staging area, ready to be committed.

Everything apart from the roof is committed. The stage now contains roof, and nothing is untracked. The log shows five commits.

Commit the roof

(HEAD -> main) * aa29e29 - put a roof on * 64d5510 - paint frames red * 31e220a - add gables+frames * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

git commit -m "put a roof on"

Commit the staged changes: now the house has a roof, and it’s in its history.

Everything is committed. The stage is empty, and nothing is untracked. The log shows six commits.

HEAD automatically updates to point at the new commit, the end of the current branch.

Add a chimney (and a hole in the roof)

(HEAD -> main) * aa29e29 - put a roof on * 64d5510 - paint frames red * 31e220a - add gables+frames * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

Adding and modifying/removing in the same change

To add the chimney, you also have to change two of the existing roof tiles. To see this more clearly, hide the chimney bricks and flick between everything, committed and untracked.

Adding the chimney is an example of a change that comprises both tracked and untracked bricks: the roof tiles are already in version control.

A difference between Lego brick and code changes: in practice you can’t change Lego bricks’ colour or size (only remove them and replace them with new ones), but the analogy is still useful. You’ve seen this already when changing the colour of the frames.

Stage the chimney changes

(HEAD -> main) * aa29e29 - put a roof on * 64d5510 - paint frames red * 31e220a - add gables+frames * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

git add chimney changes

Add the changes — the new, untracked bricks and the changes to the roof tiles — to the staging area ready to be committed.

Remember you’re staging changes, not simply new items. The staging area can contain additions, modifications, and removals.

The committed house has no chimney (and no hole in the roof). The staging area has the new chimney bricks as well as changes to the (existing) roof tiles. Nothing is untracked because all the changes have been added to the stage. The history contains six commits.

Commit the chimney

(HEAD -> main) * d234e2a - build chimney * aa29e29 - put a roof on * 64d5510 - paint frames red * 31e220a - add gables+frames * 99ebb21 - add floor * c730af3 - add walls * 544d21f - initial commit

git commit -m "build chimney"

This adds building the chimney (which means adding the new bricks as well as making the hole in the roof for it), to the history.

The history records precisely what changes occurred between each commit. Staging is the tool you use control which changes are included when you make the commit, so staging activity is not recorded: what matters is only what is staged when you make the commit.

Everything has been committed. Nothing is staged or untracked, and there are seven commits in this branch, which are shown in the log.

HEAD has automatically moved to point to the new commit, which is the end of the current branch (main).

The house has been built — with one storey and a chimney. To see a different outcome, see the branches worked example.