Chapter 4
Multiple Commits
A new commit does not have to accept any changes from the workspace. In fact, Git gives the user full control here. In the extreme case, it can be determined what changes should be incorporated in the next commit.
A commit occurs in two steps. First, all changes are collected in a buffer using the add command. This buffer is called the staging area or index. Next, the changes from the staging area are transferred to the repository using the commit command.
Figure 4.1: The route of changes in the repository
The status Command
The status command shows what changes are currently available in the workspace and which of them are already registered in the staging area for the next commit.
> git status # On branch staging # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # modified: bar.txt # # Changed but not updated: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in ... # # modified: foo.txt # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # new.txt no changes added to commit (use "git add" and/or "git commit -a")
The output shows various headings:
It helps that Git indicates what command can be used to change the status again. For example, you can use this command to unstage blah.txt.
git reset HEAD blah.txt
CVS and Subversion users may find the use of the term update here confusing. In those systems, “update” means the acquisition of changes from the repository into the workspace. In Git, however, update is the incorporation of changes from the workspace into the staging area. This is exactly the opposite direction. Babylon sends its regards.
If there are many changes, you can use the --short option to make the output more compact. For instance,
> git status --short M blah.txt M foo.txt M bar.txt ?? new-file.txt
Step by Step
Selective Commits
A new commit is created. However, it should not include all updates. Here you can select all files or some files only.
1. View the changes
> git status
Under the headings “Changed but not updated” and “Untracked files” the status command shows the files that are not yet registered for the next commit.
2. Collect the changes
Use the add command to add the changes to the staging area. You can specify the file paths individually. Or, you can specify a directory to include new and changed files from all subdirectories. The add command can be called many times and you can also use wildcards * and ? to specify paths.
> git add foo.txt bar.txt # selected files > git add dir/ # a directory and everything underneath > git add. # current directory and everything underneath
If you want even finer control, you can use the interactive mode by using the --interactive option. Then, you can select individual code fragments and, in extreme cases, individual lines of code to register for the commit.
3. Create a commit
Finally, apply the changes with a commit.
> git commit
After this the staging area will be empty. The workspace is not affected by the commit. Changes that were not added with add, remain in the workspace.
Selective commits can be very useful to separate changes from one another. Example: A new class has been created. Alongside a few mistakes in other classes have been corrected. Separating the changes into several commits makes the history clearer and makes it easier to selectively deliver single bug fixes earlier (cherry-picking).
However, you should keep in mind that selective commits can create in the repository software versions that have never existed locally. They have therefore never been tested and in the worst case may not even compile. We recommend that you avoid selective commits if possible. It is often sufficient to make a note that you want to fix an error, rather than fix it right away.
The Staging Area Stores Snapshots
One thing you should know about the staging area: it is more than just a list of files for the next commit. It stores not only locations of changes, but also what have changed. For this purpose Git generates a snapshot of the affected files with exactly the selected changes. Figure 4.2 illustrates this. In Line 1, the workspace, the staging area and the repository are still the same. Then, the developer working on the file makes some changes in his workspace (Line 2). He then uses the add command to transfer the changes to the staging area, but not yet to the repository (Line 3)
In Line 4, the developer changes the file again. Now all the three areas have different contents. A commit command transmits the first change to the repository (Line 5). The second change is still in the workspace. The developer then uses the add command to transfer it to the staging area (Line 6).
Step by Step
What Is in the Staging Area? What Is Not?
Changes were registered using the add command for the next commit. Afterward, further changes were made in the workspace. The diff command shows what is going on.
a. What is in the staging area?
Changes that have already been added using the add command to the staging area with the --staged option will be shown as staged. The following command displays the differences between the staging area and the current HEAD commit in the repository.
> git diff --staged # staging vs. repository
b. What is not registered yet?
Without any options, the diff command shows the local changes in the workspace that are not registered yet, in other words the difference between the staging area and the workspace.
> git diff # staging vs. workspace
Figure 4.2: Changes from the workspace are copied to the staging area and to the repository
What To Do with Changes That Are Not To Be Committed?
As it happens, there are things that you do not want to commit, including:
Git offers several ways to deal with them:
Step by StepUndoing changes from the Staging Area
The reset command resets the staging area. The first parameter HEAD indicates that it is to be reset to the current HEAD version. The second parameter specifies which files or directories are to be reset.
> git reset HEAD .
or
> git reset HEAD foo.txt src/test/
The staging area is overwritten during reset. Usually this is not a problem, because the same changes are likely still in the workspace (see Figure 4.2). If the same files were further processed after the add command, information may be lost.
Leaving out Unversioned Files with .gitignore
In general, generated files, temporary files created by editors or backups are files you do not want to put under version control. By adding entries in a .gitignore file in the root directory of the project you can make these files “invisible” to Git. You can specify file paths and directories in it. The wildcard characters “*” and “&” can also be used. You should know the following about paths: A simple path like generated/ will make directories that contain the name, such as src/demo/generated, ignored everywhere, If the path is prefixed with a /, such as in /generated/, then only the exact path—relative to the project’s root directory—will be ignored.
# # Simple file path # somehow/simultaneous.txt # # Directories ending with a "/" # generated/ # # File types as glob expressions # *.bak # # "!" marked exceptions. "demo.bak" # will be versioned, but "*.bak"' # will be excluded. # !demo.bak
You can create a .gitignore file in a subdirectory of your project. In this case, it will affect all files and paths below that directory. This may be useful, for example, if your project is comprised of various programming languages, each of which needs a different configuration.
Note that entries in .gitignore only affect files that are not yet managed by Git. If a file is already versioned, the status command will display all changes to it, and these can also be registered with the add command for the next commit. If you want to ignore files that are already versioned, you can do this with the --assume-unchanged option of the update-index command.
Stashing
If you are in the middle of something and, say, a fix needs to be written quickly, you often want to start making changes but not commit what you have been doing. In this case the stash command can help save changes locally and those changes can be referred to again later.
Step by Step
Saving Changes
The stash command saves changes in the workspace and the staging area in a buffer called the stash stack.
> git stash
Step by Step
Getting Back to Stashed ChangesStashed changes can be retrieved from the stack to the workspace using the stash pop command.
a. Retrieve the top-most stashed change
> git stash pop
b.1. What Is in the Stash Stack?
First, check to see what changes have been stashed.
> git stash list
stash@{0}: WIP on master: 297432e Mindmap updated. stash@{1}: WIP on omaster: 213e335 Introduction to workflow
b.2. Retrieve Older Stashed Changes
First, check to see what changes have been stashed.
> git stash pop stash@{1}
Summary
3.14.251.57