1.2. Using Git¶
When you are programming, you will make mistakes. If you program long enough, these will eventually include shenanigans like accidentally deleting all of your source files. You are also likely to spend some of your time trying out things that don’t work, at the end of which you’d like to go back to the last version of your program that did work. All these problems can be solved by using a version control system.
A brief summary of git is given below. For more details, see the tutorials available at http://git-scm.com.
Whatever version control software you use, they all follow the same basic pattern:
Initialize repository
You only need to do this step once.
If you already have a repository available on a remote server, then you can use
git clone
instead ofgit init
.Work with repository
Create new files
Add files to repository
Edit existing files
Commit changes
Push local changes to a remote server like GitHub for safe keeping.
Repeat the above as often as needed.
1.2.1. Initialize a git repository¶
Typically you run git inside a directory that holds some project you are working on (for example, homework). Before you can do anything with git, you need to create or copy a repository, which is a hidden directory .git that records changes to your files.
In this example, we will be walking though a small empty repository.
If we choose to put it in a new directory git-demo:
$ mkdir git-demo
$ cd git-demo/
$ git init
Then we should see something like:
Initialized empty Git repository in /home/runner/WorrisomeSophisticatedCybernetics/git-demo/.git
Now let’s create a file and use git add
to add it to the repository:
$ echo 'int main(int argc, char** argv) { return 0; }' > tiny.cpp
$ git add tiny.cpp
The git status
command will tell us that Git knows about tiny.cpp
,
but hasn’t committed the changes to the repository yet:
$ git status
On branch master
Initial commit
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: tiny.cpp
The git commit
command will save the actual changes,
along with a message saying what you did.
For short messages,
the easiest way to do this is to include the message on the command line:
$ git commit -m 'a very short c++ program'
[master (root-commit) 3a6fd19] a very short c++ program
1 file changed, 1 insertion(+)
create mode 100644 tiny.cpp
Without the -m
argument, git runs the default editor (vim) to let you edit your commit message.
If you don’t like vim, you can change the default using git config
:
$ git config --global core.editor "emacs -nw"
You can see what commits made so far using git log
:
$ git log
commit 3a6fd19e8ef4662744bd41a20cde9924aad918ed
Author: DaveParillo <DaveParillo@noreply.github.com>
Date: Sat Jun 10 12:07:51 2017 -0700
a very short c++ program
Try This!
Head on over to replit.com and use the console window in a repl and practice the steps described in this section.
You can run git commands in repl’s for any language,
but in order to compile tiny.cpp
, you’ll need to be in a C++ repl.
1.2.2. Editing files¶
Suppose I edit tiny.cpp using my favorite editor to turn it into the classic hello-world program:
#include <iostream>
int main() {
std::cout << "Hello, world!\n";
return 0;
}
I can see what files have changed using git status:
$ git status
On branch master
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: tiny.cpp
no changes added to commit (use "git add" and/or "git commit -a")
Notice how Git reminds me to use git commit -a
to include these changes in my next commit.
I can also do git add tiny.cpp
to only include the changes to tiny.cpp
(maybe I made changes to a different file that I want to commit separately).
If I want to know the details of the changes since my last commit, I can run git diff
:
$ git diff
diff --git a/tiny.cpp b/tiny.cpp
index a9b8738..a6501a7 100644
--- a/tiny.cpp
+++ b/tiny.cpp
@@ -1 +1,6 @@
-int main(int argc, char** argv) { return 0; }
+#include <iostream>
+
+int main() {
+ std::cout << "Hello, world!\n";
+ return 0;
+}
Since I like these changes, I commit them:
$ git commit -a -m 'turn tiny into a basic hello world'
[master 170eaf0] turn tiny into a basic hello world
1 file changed, 6 insertions(+), 1 deletion(-)
The repository now contains two commits:
$ git log | more
commit 170eaf0461a7f0f865328b73bee6d313c3dbad42
Author: DaveParillo <DaveParillo@noreply.github.com>
Date: Sat Jun 10 12:23:55 2017 -0700
turn tiny into a basic hello world
commit 3a6fd19e8ef4662744bd41a20cde9924aad918ed
Author: DaveParillo <DaveParillo@noreply.github.com>
Date: Sat Jun 10 12:07:51 2017 -0700
a very short c++ program
1.2.3. Renaming files¶
You can rename a file with git mv
.
This is just like the regular Linux mv
command,
except that it tells Git what you are doing.
If you forget to use git mv
it’s not normally a problem.
Unless your changes are massive, git is usually good about
figuring out when files have been moved:
$ git mv tiny.cpp hello.cpp
buffy:~/git-demo
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
renamed: tiny.cpp -> hello.cpp
Moving a file counts as a change. These changes don’t get written to the repository unless you do another git commit:
$ git commit -m 'give better name to hello program'
[master 7a603f4] give better name to hello program
1 file changed, 0 insertions(+), 0 deletions(-)
rename tiny.cpp => hello.cpp (100%)
1.2.4. Adding and removing files¶
To add a file, create it and call git add
:
$ cp hello.cpp goodbye.cpp
$ git status
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
goodbye.cpp
nothing added to commit but untracked files present (use "git add" to track)
$ git add goodbye.cpp
$ git commit -m 'started to make a second program to say goodbye'
[master f41cb3a] started to make a second program to say goodbye
1 file changed, 6 insertions(+)
create mode 100644 goodbye.cpp
If you add many files at once, you can refer to the directory they are in.
If that directory is the current directory, .
is acceptable.
When you specify a directory, then all of the files new or modified are
added recursively from that point downward.
Git add and commit best practices
It is easy to inadvertently add files you did not mean to when adding a directory.
Check what you have added using git status
to ensure the files
you are about to commit belong in the commit.
If you accidentally add files you did not mean to, then it is easy to
“un-add” them using git revert
.
General rules for adding files:
Commit only source files you have created or modified.
Avoid committing binary files and generated build artifacts.
To remove a file, use git rm
:
$ git rm goodbye.cpp
rm 'goodbye.cpp'
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: goodbye.cpp
$ git commit -m 'on second thought, goodbye.cpp was a bad idea'
[master cbcf75f] on second thought, goodbye.cpp was a bad idea
1 file changed, 6 deletions(-)
delete mode 100644 goodbye.cpp
1.2.5. Recovering files from the repository¶
Nothing is ever truly deleted from the repository once checked in. If you accidentally delete something, you can recover it from the repository.:
$ ls -a
./ ../ .git/ hello.cpp
$ rm hello.cpp
$ ls -a
./ ../ .git/
# gone, but not forgotten
$ git checkout -- hello.cpp
$ ls -a
./ ../ .git/ hello.cpp
Using git checkout --
gets the most recent version out of the repository,
but using the commit id, we can operate on any version:
$ git checkout 3a6f -- tiny.cpp
$ ls -a
./ ../ .git/ hello.cpp tiny.cpp
Because tiny.cpp is not part of the current HEAD (most recent version), it is considered a new file, but the checkout did add tiny.cpp and stage it for commit:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: tiny.cpp
More to Explore
-
All Git commands take a
--help
argument that brings up their manual page. There is also extensive documentation at http://git-scm.com.