11.5. Creating a Git Repo on Your Computer¶
Git is a distributed version control system, which means it stores all of its snap- shots and repo metadata locally on your computer in a folder named .git. Unlike a centralized version control system, Git doesn’t need to connect to a server over the internet to make commits. This makes Git fast and available to work with when you’re offline.
From a terminal, run the following commands to create the .git folder. (On macOS and Linux, you’ll need to run mkdir instead of md .)
C:UsersAl>md wizcoin C:UsersAl>cd wizcoin C:UsersAlwizcoin>git init Initialized empty Git repository in C:/Users/Al/wizcoin/.git/
When you convert a folder into a Git repo by running git init , all the files in it start as untracked. For our wizcoin folder, the git init command creates the wizcoin/.git folder, which contains the Git repo metadata. The presence of this .git folder makes a folder a Git repository; without it, you simply have a collection of source code files in an ordinary folder. You’ll never have to directly modify the files in .git, so just ignore this folder. In fact, it’s named .git because most operating systems automatically hide fold- ers and files whose names begin with a period.
Now you have a repo in your C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`wizcoin `working directory. A repo on your computer is known as a local repo; a repo located on someone else’s computer is known as a remote repo. This distinction is important, because you’ll often have to share commits between local and remote repos so you can work with other developers on the same project.
You can now use the git command to add files and track changes within the working directory. If you run git status in your newly created repo, you’ll see the following:
C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git status On branch master No commits yet nothing to commit (create/copy files and use “git add” to track)
The output from this command informs you that you have no commits yet in this repo.
RUNNING GIT S TAT US W ITH THE WATCH COMM A ND
While using the Git command line tool, you’ll often run git status to see your repo’s status. Instead of entering this command manually, you can use the watch command to run it for you. The watch command runs a given command repeat- edly every two seconds, refreshing the screen with its latest output.
On Windows, you can obtain the watch command by downloading https://inventwithpython.com/watch.exe and placing this file into a PATH folder, such as C::raw-latex:`\Windows`. On macOS, you can go to https://www.macports.org/ to download and install MacPorts, and then run sudo ports install watch . On Linux, the watch command is already installed. Once it’s installed, open a new Command Prompt or Terminal window, run cd to change directory to your Git repo’s project folder, and run watch “git status” . The watch command will run git status every two seconds, displaying the latest results onscreen. You can leave this window open while you use the Git command line tool in a different Terminal window to see how your repo’s status changes in real time. You can open another Terminal window and run watch “git log –online” to view a summary of the commits you make, also updated in real time. This information helps remove the guesswork as to what the Git commands you enter are doing to your repo.
11.5.1. Adding Files for Git to Track¶
Only tracked files can be committed, rolled back, or otherwise interacted with through the git command. Run git status to see the status of the files in the project folder:
C:UsersAlwizcoin>git status On branch master No commits yet Untracked files:
- (use "git add <file>..." to include in what will be committed)
.coveragerc .gitignore LICENSE.txt README.md
- --snip--
tox.ini
nothing added to commit but untracked files present (use "git add" to track)
All the files in the wizcoin folder are currently untracked 1. We can track them by doing an initial commit of these files, which takes two steps: running git add for each file to be committed, and then running git commit to create a commit of all these files. Once you’ve committed a file, Git tracks it.
The git add command moves files from the untracked state or modified state to the staged state. We could run git add for every file we plan to stage (for example, git add .coveragerc , git add .gitignore , git add LICENSE.txt , and so on), but that’s tedious. Instead, let’s use the * wildcard to add several files at once. For example, git add *.py adds all .py files in the current working directory and its subdirectories. To add every untracked file, use a single period ( . ) to tell Git to match all files:
C:UsersAlwizcoin>git add .
Run git status to see the files you’ve staged:
C:UsersAlwizcoin>git status On branch master No commits yet Changes to be committed:
- (use "git rm --cached <file>..." to unstage)
new file: .coveragerc new file: .gitignore
- --snip--
new file:tox.ini
The output of git status tells you which files are staged to be commit- ted the next time you run git commit 1. It also tells you that these are new files added to the repo 2 rather than existing files in the repo that have been modified.
After running git add to select the files to add to the repo, run git commit –m “Adding new files to the repo.” (or a similar commit message) and git status again to view the repo status:
C:UsersAlwizcoin>git commit -m "Adding new files to the repo." [master (root-commit) 65f3b4d] Adding new files to the repo. 15 files changed, 597 insertions(+) create mode 100644 .coveragerc create mode 100644 .gitignore --snip-- create mode 100644 tox.ini C:UsersAlwizcoin>git status On branch master nothing to commit, working tree clean
Note that any files listed in the .gitignore file won’t be added to staging, as I explain in the next section. ## Ignoring Files in the Repo Files not tracked by Git appear as untracked when you run git status . But in the course of writing your code, you might want to exclude certain files from version control completely so you don’t accidentally track them. These include:
Temporary files in the project folder
The .pyc, .pyo, and .pyd files that the Python interpreter generates when it runs .py programs
The .tox, htmlcov, and other folders that various software development tools generate docs/_build
Any other compiled or generated files that could be regenerated(because the repo is for source files, not the products created from source files)
Source code files that contain database passwords, authentication tokens, credit card numbers, or other sensitive information
To avoid including these files, create a text file named .gitignore that lists the folders and files that Git should never track. Git will automatically exclude these from git add or git commit commands, and they won’t appear when you run git status .
The .gitignore file that the cookiecutter-basicpythonproject template cre- ates looks like this:
# Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class --snip--
The .gitignore file uses * for wildcards and # for comments. You can read more about it in the online documentation at https://git-scm.com/docs/ gitignore.
You should add the actual .gitignore file to the Git repo so other pro- grammers have it if they clone your repo. If you want to see which files in your working directory are being ignored based on the settings in .gitignore, run the git ls-files –other –ignored –exclude-standard command. ## Committing Changes
After adding new files to the repo, you can continue writing code for your project. When you want to create another snapshot, you can run git add . to stage all modified files and git commit –m <commit message> to commit all staged files. But doing so is easier with the single git commit –am <commit message> command:
C:UsersAlwizcoin>git commit -am "Fixed the currency conversion bug." [master (root-commit) e1ae3a3] Fixed the currency conversion bug. 1 file changed, 12 insertions(+)
If you want to commit only certain modified files instead of every modi- fied file, you can omit the –a option from –am and specify the files after the commit message, such as git commit –m file1.py file2.py .
The commit message provides a hint for future use: it’s a reminder about what changes we made in this commit. It might be tempting to write a short, generic message, such as “Updated code,” or “Fixed a few bugs,” or even just “x” (because blank commit messages aren’t allowed). But three weeks from now, when you need to roll back to an earlier version of your code, detailed commit messages will save you a lot of grief in determining exactly how far back you need to go.
If you forget to add the -m “” command line argument, Git will open the Vim text editor in the Terminal window. Vim is beyond the scope of this book, so press the ESC key and enter qa! to safely exit Vim and can- cel the commit. Then enter the git commit command again, this time with the -m “” command line argument.
For examples of what professional commit messages look like, check out the commit history for the Django web framework at https://github.com/django/ django/commits/master. Because Django is a large, open source project, the com- mits occur frequently and are formal commit messages. Infrequent commits with vague commit messages might work well enough for your small, personal programming projects, but Django has more than 1,000 contributors. Poor commit messages from any of them becomes a problem for all of them.
The files are now safely committed to the Git repo. Run git status one more time to view their status:
C:UsersAlwizcoin>git status On branch master nothing to commit, working tree clean
By committing the staged files, you’ve moved them back to the com- mitted state, and Git tells us that the working tree is clean; in other words, there are no modified or staged files. To recap, when we added files to the Git repo, the files went from untracked to staged and then to committed. The files are now ready for future modifications.
Note that you can’t commit folders to a Git repo. Git automatically includes folders in the repo when a file in them is committed, but you can’t commit an empty folder.
If you made a typo in the most recent commit message, you can rewrite it using the git commit –amend -m “<new commit message>” command. ## Using git diff to View Changes Before Committing Before you commit code, you should quickly review the changes you’ll com- mit when you run git commit . You can view the differences between the code currently in your working copy and the code in the latest commit using the git diff command.
Let’s walk through an example of using git diff . Open README.md in a text editor or IDE. (You should have created this file when you ran Cookiecutter. If it doesn’t exist, create a blank text file and save it as README.md.) This is a Markdown-formatted file, but like Python scripts, it’s written in plaintext. Change the TODO - fill this in later text in the Quickstart Guide section to the following (keep the xample typo in it for now; we’ll fix it later):
11.5.2. Quickstart Guide¶
Here's some xample code demonstrating how this module is used: >>> import wizcoin >>> coin = wizcoin.WizCoin(2, 5, 10) >>> str(coin) '2g, 5s, 10k' >>> coin.value() 1141
Before we add and commit README.md, run the git diff command to see the changes we’ve made:
C:UsersAlwizcoin>git diff diff --git a/README.md b/README.md index 76b5814..3be49c3 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,14 @@ To install with pip, run: Quickstart Guide ---------------- -TODO - fill this in later +Here's some xample code demonstrating how this module is used: + + >>> import wizcoin + >>> coin = wizcoin.WizCoin(2, 5, 10) + >>> str(coin) + '2g, 5s, 10k' + >>> coin.value() + 1141 Contribute ----------
The output shows that README.md in your working copy has changed from the README.md as it exists in the latest commit of the repo. The lines that begin with a minus sign – have been removed; the lines that begin with a plus sign + have been added.
While reviewing the changes, you’ll also notice that we made a typo by writing xample instead of example . We shouldn’t check in this typo. Let’s cor- rect it. Then run git diff again to inspect the change and add and commit it to the repo:
C:UsersAlwizcoin>git diff diff --git a/README.md b/README.md index 76b5814..3be49c3 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,14 @@ To install with pip, run: Quickstart Guide ---------------- -TODO - fill this in later +Here's some example code demonstrating how this module is used: --snip-- C:UsersAlwizcoin>git add README.md C:UsersAlwizcoin>git commit -m "Added example code to README.md" [master 2a4c5b8] Added example code to README.md 1 file changed, 8 insertions(+), 1 deletion(-)
The correction is now safely committed to the repo. Using git difftool to View Changes with a GUI Application It’s easier to see changes with a diff program that uses a GUI. On Windows, you can download WinMerge (https://winmerge.org/), a free, open source diff program, and then install it. On Linux, you can install either Meld by using the sudo apt-get install meld command or Kompare by using the sudo apt- get install kompare command. On macOS, you can install tkdiff by using commands that first install and configure Homebrew (a package manager that installs software) and then using Homebrew to install tkdiff:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/ master/install.sh)" brew install tkdiff
You can configure Git to use these tools by running git config diff.tool , where is winmerge , tkdiff , meld , or kompare . Then run git difftool <filename> to view the changes made to a file in the tool’s GUI, as shown in Figure 12-5.

Figure 12-5: A GUI diff tool, in this case WinMerge, is more readable than the text output of git diff.
Additionally, run git config –global difftool.prompt false so Git doesn’t ask for confirmation each time you want to open the diff tool. If you installed a GUI Git client, you can also configure it to use these tools (or it might come with a visual diff tool of its own). ## How Often Should I Commit Changes? Even though version control allows you to roll back your files to an ear- lier commit, you might wonder how often you should make commits. If you commit too frequently, you’ll have trouble sorting through a large number of insignificant commits to find the version of the code you’re looking for. If you commit too infrequently, each commit will contain a large number of changes, and reverting to a particular commit will undo more changes than you want to. In general, programmers tend to commit less frequently than they should.
You should commit code when you’ve completed an entire piece of functionality, such as a feature, class, or bug fix. Don’t commit any code that contains syntax errors or is obviously broken. Commits can consist of a few lines of changed code or several hundred, but either way, you should be able to jump back to any earlier commit and still have a working program. You should always run any unit tests before committing. Ideally, all your tests should pass (and if they don’t pass, mention this in the commit message). ## Deleting Files from the Repo If you no longer need Git to track a file, you can’t simply delete the file from the filesystem. You must delete it through Git using the git rm command, which also tells Git to untrack the file. To practice doing so, run the echo “Test file” > deleteme.txt command to create a small file named deleteme. txt with the contents “Test file” . Then commit it to the repo by running the following commands:
C:UsersAlwizcoin>echo "Test file" > deleteme.txt C:UsersAlwizcoin>git add deleteme.txt C:UsersAlwizcoin>git commit -m "Adding a file to test Git deletion." [master 441556a] Adding a file to test Git deletion. 1 file changed, 1 insertion(+) create mode 100644 deleteme.txt C:UsersAlwizcoin>git status On branch master nothing to commit, working tree clean
Don’t delete the file using the del command on Windows or rm com- mand on macOS and Linux. (If you do, you can run git restore to recover it or simply continue with the git rm command to remove it from the repo.) Instead, use the git rm command to delete and stage the deleteme.txt file such as in this example:
C:UsersAlwizcoin>git rm deleteme.txt rm deleteme.txt'
The git rm command deletes the file from your working copy, but you’re not done yet. Like git add , the git rm command stages the file. You need to commit file deletion just like any other change:
C:UsersAlwizcoin>git status On branch master Changes to be committed: 1 (use "git reset HEAD <file>..." to unstage) deleted:
deleteme.txtC:UsersAlwizcoin>git commit -m "Deleting deleteme.txt from the repo to finish the deletion test." [master 369de78] Deleting deleteme.txt from the repo to finish the deletion test. 1 file changed, 1 deletion(-) delete mode 100644 deleteme.txt C:UsersAlDesktopwizcoin>git status On branch master nothing to commit, working tree clean
Even though you’ve deleted deleteme.txt from your working copy, it still exists in the repo’s history. The “Recovering Old Changes” section later in this chapter describes how to recover a deleted file or undo a change.
The git rm command only works on files that are in the clean, commit- ted state, without any modifications. Otherwise, Git asks you to commit the changes or revert them with the git reset HEAD <filename> command. (The output of git status reminds you of this command 1.) This procedure pre- vents you from accidentally deleting uncommitted changes. ## Renaming and Moving Files in the Repo Similar to deleting a file, you shouldn’t rename or move a file in a repo unless you use Git. If you try to do so without using Git, it will think you deleted a file and then created a new file that just happens to have the same content. Instead, use the git mv command, followed by git commit . Let’s rename the README.md file to README.txt by running the following commands:
C:UsersAlwizcoin>git mv README.md README.txt C:UsersAlwizcoin>git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) renamed: README.md -> README.txt C:UsersAlwizcoin>git commit -m "Testing the renaming of files in Git." [master 3fee6a6] Testing the renaming of files in Git. 1 file changed, 0 insertions(+), 0 deletions(-) rename README.md => README.txt (100%)
This way, the history of changes to README.txt also includes the his- tory of README.md.
We can also use the git mv command to move a file to a new folder. Enter the following commands to create a new folder called movetest and move the README.txt into it:
C:UsersAlwizcoin>mkdir movetest C:UsersAlwizcoin>git mv README.txt movetest/README.txt C:UsersAlwizcoin>git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) renamed: README.txt -> movetest/README.txt C:UsersAlwizcoin>git commit -m "Testing the moving of files in Git." [master 3ed22ed] Testing the moving of files in Git. 1 file changed, 0 insertions(+), 0 deletions(-) rename README.txt => movetest/README.txt (100%)
You can also rename and move a file at the same time by passing git mv a new name and location. Let’s move the README.txt back to its original place at the root of the working directory and give it its original name:
C:UsersAlwizcoin>git mv movetest/README.txt README.md C:UsersAlwizcoin>git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) renamed: movetest/README.txt -> README.md C:UsersAlwizcoin>git commit -m "Moving the README file back to its original place and name." [master 962a8ba] Moving the README file back to its original place and name. 1 file changed, 0 insertions(+), 0 deletions(-) rename movetest/README.txt => README.md (100%)
Note that even though the README.md file is back in its original folder and has its original name, the Git repo remembers the moves and name changes. You can see this history using the git log command, described in the next section.
C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git diff diff –git a/README.md b/README.md index 76b5814..3be49c3 100644 — a/README.md +++ b/README.md @@ -13,7 +13,14 @@ To install with pip, run: Quickstart Guide —————- -TODO - fill this in later +Here’s some example code demonstrating how this module is used: –snip– C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git add README.md C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git commit -m “Added example code to README.md” [master 2a4c5b8] Added example code to README.md 1 file changed, 8 insertions(+), 1 deletion(-) The correction is now safely committed to the repo. Using git difftool to View Changes with a GUI Application It’s easier to see changes with a diff program that uses a GUI. On Windows, you can download WinMerge (https://winmerge.org/), a free, open source diff program, and then install it. On Linux, you can install either Meld by using the sudo apt-get install meld command or Kompare by using the sudo apt- get install kompare command. On macOS, you can install tkdiff by using commands that first install and configure Homebrew (a package manager that installs software) and then using Homebrew to install tkdiff:
/bin/bash -c “$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/ master/install.sh)” brew install tkdiff You can configure Git to use these tools by running git config diff.tool , where is winmerge , tkdiff , meld , or kompare . Then run git difftool to view the changes made to a file in the tool’s GUI, as shown in Figure 12-5. Figure 12-5: A GUI diff tool, in this case WinMerge, is more readable than the text output of git diff. Additionally, run git config –global difftool.prompt false so Git doesn’t ask for confirmation each time you want to open the diff tool. If you installed a GUI Git client, you can also configure it to use these tools (or it might come with a visual diff tool of its own). How Often Should I Commit Changes? Even though version control allows you to roll back your files to an ear- lier commit, you might wonder how often you should make commits. If you commit too frequently, you’ll have trouble sorting through a large number of insignificant commits to find the version of the code you’re looking for. If you commit too infrequently, each commit will contain a large number of changes, and reverting to a particular commit will undo more changes than you want to. In general, programmers tend to commit less frequently than they should. You should commit code when you’ve completed an entire piece of functionality, such as a feature, class, or bug fix. Don’t commit any code that contains syntax errors or is obviously broken. Commits can consist of a few lines of changed code or several hundred, but either way, you should be able to jump back to any earlier commit and still have a working program. You should always run any unit tests before committing. Ideally, all your tests should pass (and if they don’t pass, mention this in the commit message). Deleting Files from the Repo If you no longer need Git to track a file, you can’t simply delete the file from the filesystem. You must delete it through Git using the git rm command, which also tells Git to untrack the file. To practice doing so, run the echo “Test file” > deleteme.txt command to create a small file named deleteme. txt with the contents “Test file” . Then commit it to the repo by running the following commands:
C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>echo “Test file” > deleteme.txt C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git add deleteme.txt C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git commit -m “Adding a file to test Git deletion.” [master 441556a] Adding a file to test Git deletion. 1 file changed, 1 insertion(+) create mode 100644 deleteme.txt C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git status On branch master nothing to commit, working tree clean Don’t delete the file using the del command on Windows or rm com- mand on macOS and Linux. (If you do, you can run git restore to recover it or simply continue with the git rm command to remove it from the repo.) Instead, use the git rm command to delete and stage the deleteme.txt file such as in this example: C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git rm deleteme.txt rm deleteme.txt’ The git rm command deletes the file from your working copy, but you’re not done yet. Like git add , the git rm command stages the file. You need to commit file deletion just like any other change: C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git status On branch master Changes to be committed: 1 (use “git reset HEAD …” to unstage) deleted:
deleteme.txtC::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git commit -m “Deleting deleteme.txt from the repo to finish the deletion test.” [master 369de78] Deleting deleteme.txt from the repo to finish the deletion test. 1 file changed, 1 deletion(-) delete mode 100644 deleteme.txt C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\Desktop`:raw-latex:`\wizcoin`>git status On branch master nothing to commit, working tree clean Even though you’ve deleted deleteme.txt from your working copy, it still exists in the repo’s history. The “Recovering Old Changes” section later in this chapter describes how to recover a deleted file or undo a change. The git rm command only works on files that are in the clean, commit- ted state, without any modifications. Otherwise, Git asks you to commit the changes or revert them with the git reset HEAD command. (The output of git status reminds you of this command 1.) This procedure pre- vents you from accidentally deleting uncommitted changes. Renaming and Moving Files in the Repo Similar to deleting a file, you shouldn’t rename or move a file in a repo unless you use Git. If you try to do so without using Git, it will think you deleted a file and then created a new file that just happens to have the same content. Instead, use the git mv command, followed by git commit . Let’s rename the README.md file to README.txt by running the following commands:
C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git mv README.md README.txt C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git status On branch master Changes to be committed: (use “git reset HEAD …” to unstage) renamed: README.md -> README.txt C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git commit -m “Testing the renaming of files in Git.” [master 3fee6a6] Testing the renaming of files in Git. 1 file changed, 0 insertions(+), 0 deletions(-) rename README.md => README.txt (100%) This way, the history of changes to README.txt also includes the his- tory of README.md. We can also use the git mv command to move a file to a new folder. Enter the following commands to create a new folder called movetest and move the README.txt into it:
C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>mkdir movetest C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git mv README.txt movetest/README.txt C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git status On branch master Changes to be committed:
(use “git reset HEAD …” to unstage) renamed: README.txt -> movetest/README.txt C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git commit -m “Testing the moving of files in Git.” [master 3ed22ed] Testing the moving of files in Git. 1 file changed, 0 insertions(+), 0 deletions(-) rename README.txt => movetest/README.txt (100%) You can also rename and move a file at the same time by passing git mv a new name and location. Let’s move the README.txt back to its original place at the root of the working directory and give it its original name: C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git mv movetest/README.txt README.md C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git status On branch master Changes to be committed: (use “git reset HEAD …” to unstage) renamed: movetest/README.txt -> README.md C::raw-latex:`\Users`:raw-latex:`\Al`:raw-latex:`\wizcoin`>git commit -m “Moving the README file back to its original place and name.” [master 962a8ba] Moving the README file back to its original place and name. 1 file changed, 0 insertions(+), 0 deletions(-) rename movetest/README.txt => README.md (100%) Note that even though the README.md file is back in its original folder and has its original name, the Git repo remembers the moves and name changes. You can see this history using the git log command, described in the next section.