This post is part of a series:
In the previous post we have seen how to inspect the commit history of an already existing project. And in this post, we are going to see how we can create such a history in the first place. So, how we can create commits. And the project, that we are going to version-control, is actually the command-line-based Tic-Tac-Toe game that we have seen in the previous post.
So, if you want to follow along, check out the repo that we downloaded in the previous post. In there, is a folder called “supporting_materials”. And in that folder is a file called “code_snippets.py”. This contains the code snippets that I will use throughout the rest of this tutorial.
First-time Git Setup
And now, before we can start creating our first commits, we first have to set some configurations in Git. Namely, we have to configure the username and the user e-mail address. And that’s because it must be clear who the creator of a commit is.
So, let’s open the command line (I’m using Git Bash). And then, let’s use the command “git config --global”:
See slide 1
Simply replace my name and e-mail with your name and e-mail. Using the option “--global” ensures that we only have to set the username and user e-mail once. After that, those values are stored.
So now, after this setup, we can start to version-control our project. Therefore, let’s first create a folder that is going to contain the project and let’s call it “tic_tac_toe”.
See slide 2
Then, let’s go into this folder using the command line (my folder is on the desktop).
See slide 3
And now, to use Git in order to version-control this project, i.e. folder, we simply need to run the command “git init”.
See slide 4
This will create a folder called “.git” inside our “tic_tac_toe” folder.
See slide 5
This folder is called the “repository” and it will contain the commit history.
So now, we have initialized the “tic_tac_toe” folder so that we can version-control it with Git. And you can also see that in the command line. Namely, at the end of the prompt, it now states “master” in parentheses which means that we are on the master branch.
See slide 6
1st Example for Creating Commits – several Files
So now, let’s create our first commit (coding session 1 in “code_snippets.py”). Therefor, let’s create a README file which is going to be a markdown file. And at this point, it just contains the title of our project and some bullet points explaining some objectives.
See slide 7
Then, we create a second file which is a Python file. This is going to contain the helper functions that we are going to need to create the actual game. So, let’s call it “helper_functions.py”.
See slide 8
And here, we first create the game board. And the board is simply a list of lists containing zeros which is supposed to represent the 3x3 Tic-Tac-Toe grid. Then, we also create our first function called “show” which prints out the game board with some additional illustrations.
See slide 9
So now, let’s say we are satisfied with what we have created so far and that we want to save it into a commit. Before we do that, let’s first get to learn a new command, namely “git status”. This gives us some useful information about the three areas.
See slide 10
Currently, we are on branch master and we haven’t created any commits yet. After that, in red color, the files are listed that aren’t tracked by Git yet, namely the README and “helper_functions.py”.
So, referring back to our illustration of the three areas in part 1 of this series, we now have those two untracked files in the working directory.
See slide 11
And then, Git also tells us what we need to do to track those files, i.e. to version-control these files. Namely, we need to use the command “git add” followed by the name(s) of the file(s) that we want to track. (side note: This is something that you can keep in the back of your mind when working with Git: Namely, Git generally does a god job at providing information about what you need to do next.)
So, let’s now add both files to the staging area and run “git status” again.
See slide 12
Now, in green color, the files are listed under the section “Changes to be committed”. So, referring back to our illustration of the three areas, they are now both in the staging area.
See slide 13
And with that, we are finally ready to create our first commit which accordingly is going to contain those two files. Therefore, we simply need to use the command “git commit”.
See slide 14
This will open up a new tab in our text editor (side note: During the installation of Git, you had to specify which editor you want to use. I am using VS Code).
See slide 15
Here, we can enter the commit message.
See slide 16
At the first line, we can give it, so to say, a title or heading. So, in this case, let’s just call it “initial commit”. And then, if we leave an empty line, we can, so to say, write the body of the message. Here, we can then go into more detail if we want to.
Then, let’s save that and close the tab of the commit message. And with that, we have created our first commit.
See slide 17
So, let’s run “git log” again to see the commit.
See slide 18
And as we can see, it says “initial commit” and then after that is our more in-depth explanation. And if we run “git log --oneline” again, then we can see that only the title of the commit message is displayed.
See slide 19
And now, finally, let’s run “git status” again to see what it tells us now.
See slide 20
And as we can see, there is nothing to commit and the working tree is clean. So, there is nothing in the staging area and nothing in the working directory. So, the three areas look now like this:
See slide 21
2nd Example for Creating Commits – one File at a time
Let’s say in our next coding session (coding session 2 in “code_snippets.py”), we are finalizing our README. So, instead of using bullet points we are using complete sentences.
See slide 22
And then, let’s also say that we create the next function which allows us to make a move on the game board and thereby change the board.
See slide 23
So, for example, let’s say “Player 1” plays position “1, 1”. This is what the board looks like before and after the move:
See slide 24
And now, let’s run git status again.
See slide 25
And as we can see, it states that there are changes that are not staged yet for our next commit. And that’s because in the working directory, the “README.md” and the “helper_functions.py” files were modified.
So, just like before, we can use “git add” to add those files to the staging area. But this time, we are not going to put both of these files into the staging area. Instead, we are going to stage them individually and create two commits out of those two changes.
And that’s because each commit should be its own meaningful change. And what we have done in this “coding session” is, we have finalized our README and we have created another function. Those two changes don’t really have anything to do with each other. So, it makes sense to put them into separate commits.
Let’s start with the README and then let’s run “git status” again.
See slide 26
Here, we can see that the README is in the staging area. So, it is ready to be committed. And “helper_functions.py” is still not staged for committing.
As a side note, I would like to point out another useful hint provided by “git status”. Namely, in the parentheses, it mentions the command “git restore”. As stated, this command allows us to either unstage files from the staging area or to discard the changes of the modified files in the working directory.
So, if we would like to unstage the README again, then we would type “git restore --staged README.md”. And if we would like to discard the changes in “helper_functions.py” (so, the “change” function), then we would type “git restore helper_functions.py”.
Okay so now, let’s create our next commit. But this time, instead of using just the command “git commit”, we use a specific option of that command, namely “--message” or “-m” for short.
See slide 27
This allows us to immediately type our commit message into quotation marks. So, in this case, it is simply “update README”. This, however, is only the title of the commit. So, if we are making a bigger change, then it would probably better to just use “git commit” and then use the editor to write a more detailed message like we did for the “initial commit”.
So, after running the above command, let’s run “git log” again.
See slide 28
And, as we can see, we have created our second commit. But here, as just said, there is only a title and no body for the message.
So, let’s now also run “git status” again.
See slide 29
And, as we can see, only “helper_functions.py” is now listed as not having been staged for commit yet. So, let’s create a commit for the “change” function that we added to that file.
See slide 30
And now, let’s run “git log” again to see if the commit was created.
See slide 31
It was. And if we run “git status” again, we can see that there is nothing to commit and that the working tree is clear again.
See slide 32
So, that concludes the second example for creating commits. And here is a summary of what we have done:
See slides 33-37
3rd Example for Creating Commits – Part of one File
Let’s say in our next coding session (coding session 3 in “code_snippets.py”) we create a function that determines the state of the game. So, it determines if there is a winner or if the game is still ongoing.
See slide 38
And then, let’s also say we decide to actually not hard-code the game board.
See slide 39
But instead, we create a function that allows us to create a game board of a variable size.
See slide 40
So, we can still create the standard 3x3 board. But we could also, for example, create a 5x5 board.
See slide 41
Okay, so those are the two functions that we created in our coding session. And even though we created them in the same session, they aren’t really related to each other. So, accordingly we would like to put them into separate commits.
But, since both functions were added to the same file, “git status” only shows us that “helper_functions.py” was modified.
See slide 42
So, if we would add that file to the staging area, then both of the functions would be contained in the next commit. Therefore, to make sure that they go into separate commits, we need to be able to only stage part of the “helper_functions.py” file. And we can do that by using the “git add” command together with the “--patch” option.
See slide 43
This will show us changes in the code that Git thinks belong together and comprise a “hunk”.
See slide 44
So, in this case, the hard-coded game board was deleted and replaced by the “create_initial_game_board” function. Then, at the end in blue, Git asks us if we want to stage this hunk. And the options that we have to answer the question are listed in the brackets.
One of the options is a “?”. This will show us what all the options actually mean.
See slide 45
At the end, we are again asked if we want to stage this hunk. And since this hunk of code represents the changes, we have made to enable a game board of variable size, let’s type “y” and press “enter”. (side note: Sometimes the hunks that Git identifies are not exactly the pieces of the code that you want. For example, if I would have added both functions at the end of the file, then Git would have thought that they belong together into a hunk. In that case, you can choose option “e” to manually edit the hunks.)
Then, it will show us the next hunk.
See slide 46
This time, it is comprised of the “determine_game_status” function. And since this doesn’t have anything to do with enabling a game board of variable size, let’s type “n” and press “enter”.
And with that, we have now only staged part of the “helper_functions.py” file. So, let’s run “git status” again.
See slide 47
And, as we can see, “helper_functions.py” now appears twice, once in the “Changes to be committed”-section and once in the “Changes not staged for commit”-section. And that’s exactly because we only staged part of the file while the other part is still unstaged.
So now, before we actually create the commit, let’s first revisit a command that we have covered in the previous post, namely “git diff”. We can’t just use this command to compare different commits with each other, but we can also use it to see what changes we currently have either in the staging area or in the working directory.
If we use the command with the “--staged” option, then we can check the differences between the staging area and the checked-out commit.
See slide 48
So, in this case the changes are that the hard-coded game board was deleted and the “create_initial_game_board” function was added.
If we simply use “git diff”, then we can check the differences between the working directory and the staging area (Side note: if there is nothing in the staging area, then the command will check the differences between the working directory and the checked-out commit).
See slide 49
So here, the “determine_game_status” function was added.
Okay so now, let’s create a commit that contains the changes to enable a game board of variable size.
See slide 50
If we now run “git status” again, we can see that “helper_functions.py” only appears once, namely as not staged yet.
See slide 51
Those are the changes that we didn’t stage before, namely the “determine_game_status” function. So, let’s now create a commit for that.
See slide 52
And if we run “git status”, we can see that there is nothing to commit and the working tree is clear again.
See slide 53
So, that concludes the third example for creating commits. And here is a summary of what we have done:
See slides 54-58
So now, have seen three different examples of creating commits.
See slide 59
And hopefully, those examples made it clearer how we can use the staging area to create commits that represent one meaningful change.
So, with that we have now seen how to create commits. And here are the commands that we have used so far.
See slide 60
And in the next post, we are going to see how to work with branches in Git.