https://www.ntu.edu.sg/home/ehchua/programming/howto/Git_HowTo.html#zz-7.

1.  Introduction

GIT is a Version Control System (VCS) (aka Revision Control System (RCS), Source Code Manager (SCM)). A VCS serves as a Repository (or repo) of program codes, including all the historical revisions. It records changes to files at so-called commits in a log so that you can recall any file at any commit point.

Why VCS?

  1. The Repository serves as the backup (in case of code changes or disk crash).
  2. It is a living archive of all historical revisions. It lets you revert back to a specific version, if the need arises.
  3. It facilitates collaboration between team members, and serves as a project management tool.
  4. more...

Git was initially designed and developed by Linus Torvalds, in 2005, to support the development of the Linux kernel.

GIT is a Distributed Version Control System (DVCS). Other popular VCSes include:

  1. The standalone and legacy Unix's RCS (Revision Control System).
  2. Centralized Client-Server Version Control System (CVCS): CVS (Concurrent Version System), SVN (Subversion) and Perforce.
  3. Distributed VCS (DVCS): GIT, Merurial, Bazaar, Darcs.

The mother site for Git is http://git-scm.com.

2.  Setting Up Git

You need to setup Git on your local machine, as follows:

  1. Download & Install:

    • For Windows and Mac, download the installer from http://git-scm.com/downloads and run the downloaded installer.
    • For Ubuntu, issue command "sudo apt-get install git".

    For Windows, use the "Git Bash" command shell bundled with Git Installer to issue commands. For Mac/Ubuntu, use the "Terminal".

  2. Customize Git:
    Issue "git config" command (for Windows, run "Git Bash" from the Git installed directory. For Ubuntu/Mac, launch a "Terminal"):
    1. // Set up your username and email (to be used in labeling your commits)
    2. $ git config --global user.name "your-name"
    3. $ git config --global user.email "your-email@youremail.com"

    The settings are kept in "<GIT_HOME>/etc/gitconfig" (of the GIT installed directory) and "<USER_HOME>/.gitconfig" (of the user's home directory.
    You can issue "git config --list" to list the settings:

    1. $ git config --list
    2. user.email=xxxxxx@xxxxxx.com
    3. user.name=xxxxxx

3.  Git Basics

Git Commands

Git provides a set of simple, distinct, standalone commands developed according to the "Unix toolkit" philosophy - build small, interoperable tools.

To issue a command, start a "Terminal" (for Ubuntu/Mac) or "Git Bash" (for Windows):

  1. $ git <command> <arguments>

The commonly-used commands are:

  1. init, clone, config: for starting a Git-managed project.
  2. add, mv, rm: for staging file changes.
  3. commit, rebase, reset, tag:
  4. status, log, diff, grep, show: show status
  5. checkout, branch, merge, push, fetch, pull
Help and Manual

The best way to get help these days is certainly googling.

To get help on Git commands:

  1. $ git help <command>
  2. // or
  3. $ git <command> --help

The GIT manual is bundled with the software (under the "doc" directory), and also available online @ http://git-scm.com/docs.

3.1  Getting Started with Local Repo

There are 2 ways to start a Git-managed project:

  1. Starting your own project;
  2. Cloning an existing project from a GIT host.

We shall begin with "Starting your own project" and cover "Cloning" later @ "Clone a Project from a Remote Repo".

Setup the Working Directory for a New Project

Let's start a programming project under the working directory called "hello-git", with one source file "Hello.java" (or "Hello.cpp", or "Hello.c") as follows:

  1. // Hello.java
  2. public class Hello {
  3. public static void main(String[] args) {
  4. System.out.println("Hello, world from GIT!");
  5. }
  6. }

Compile the "Hello.java" into "Hello.class" (or "Hello.cpp" or "Hello.c" into "Hello.exe").

It is also highly recommended to provide a "README.md" file (a text file in a so-called "Markdown" syntax such as "GitHub Flavored Markdown") to describe your project:

  1. // README.md
  2. This is the README file for the Hello-world project.

Now, we have 3 files in the working tree: "Hello.java", "Hello.class" and "README.md". We do not wish to track the ".class" as they can be reproduced from ".java".

Initialize a new Git Repo (git init)

To manage a project under Git, run "git init" at the project root directory (i.e., "hello-git") (via "Git Bash" for Windows, or "Terminal" for Ubuntu/Mac):

  1. // Change directory to the project directory
  2. $ cd /path-to/hello-git
  3.  
  4. // Initialize Git repo for this project
  5. $ git init
  6. Initialized empty Git repository in /path-to/hello-git/.git/
  7.  
  8. $ ls -al
  9. drwxr-xr-x 1 xxxxx xxxxx 4096 Sep 14 14:58 .git
  10. -rw-r--r-- 1 xxxxx xxxxx 426 Sep 14 14:40 Hello.class
  11. -rw-r--r-- 1 xxxxx xxxxx 142 Sep 14 14:32 Hello.java
  12. -rw-r--r-- 1 xxxxx xxxxx 66 Sep 14 14:33 README.md

A hidden sub-directory called ".git" will be created under your project root directory (as shown in the above "ls -a" listing), which contains ALL Git related data.

Take note that EACH Git repo is associated with a project directory (and its sub-directories). The Git repo is completely contain within the project directory. Hence, it is safe to copy, move or rename the project directory. If your project uses more than one directories, you may create one Git repo for EACH directory, or use symlinks to link up the directories, or ... (?!).

Git Storage Model

The local repo after "git init" is empty. You need to explicitly deposit files into the repo.

Before we proceed, it is important to stress that Git manages changes to files between so-called commits. In other words, it is a version control system that allows you to keep track of the file changes at the commits.

Staging File Changes for Tracking (git add <file>...)

Issue a "git status" command to show the status of the files:

  1. $ git status
  2. On branch master
  3. Initial commit
  4.  
  5. Untracked files:
  6. (use "git add <file>..." to include in what will be committed)
  7. Hello.class
  8. Hello.java
  9. README.md
  10. nothing added to commit but untracked files present (use "git add" to track)

By default, we start on a branch called "master". We will discuss "branch" later.

In Git, the files in the working tree are either untracked or tracked. Currently, all 3 files are untracked. To stage a new file for tracking, use "git add <file>..." command.

  1. // Add README.md file
  2. $ git add README.md
  3.  
  4. $ git status
  5. On branch master
  6. Initial commit
  7.  
  8. Changes to be committed:
  9. (use "git rm --cached <file>..." to unstage)
  10. new file: README.md
  11.  
  12. Untracked files:
  13. (use "git add <file>..." to include in what will be committed)
  14. Hello.class
  15. Hello.java
  16.  
  17. // You can use wildcard * in the filename
  18. // Add all Java source files into Git repo
  19. $ git add *.java
  20.  
  21. // You can also include multiple files in the "git add"
  22. // E.g.,
  23. // git add Hello.java README.md
  24.  
  25. $ git status
  26. On branch master
  27. Initial commit
  28.  
  29. Changes to be committed:
  30. (use "git rm --cached <file>..." to unstage)
  31. new file: Hello.java
  32. new file: README.md
  33.  
  34. Untracked files:
  35. (use "git add <file>..." to include in what will be committed)
  36. Hello.class

The command "git add <file>..." takes one or more filenames or pathnames with possibly wildcards pattern. You can also use "git add ." to add all the files in the current directory (and all sub-directories). But this will include "Hello.class", which we do not wish to be tracked.

When a new file is added, it is staged (or indexed, or cached) in the staging area (as shown in the GIT storage model), but NOT yet committed.

Git uses two stages to commit file changes:

  1. "git add <file>" to stage file changes into the staging area, and
  2. "git commit" to commit ALL the file changes in the staging area to the local repo.

The staging area allows you to group related file changes and commit them together.

Committing File Changes (git commit)

The "git commit" command commits ALL the file changes in the staging area. Use a -m option to provide a message for the commit.

  1. $ git commit -m "First commit" // -m to specify the commit message
  2. [master (root-commit) 858f3e7] first commit
  3. 2 files changed, 8 insertions(+)
  4. create mode 100644 Hello.java
  5. create mode 100644 README.md
  6.  
  7. // Check the status
  8. $ git status
  9. On branch master
  10. Untracked files:
  11. (use "git add <file>..." to include in what will be committed)
  12. Hello.class
  13. nothing added to commit but untracked files present (use "git add" to track)
Viewing the Commit Data (git log)

Git records several pieces of metadata for every commit, which includes a log message, timestamp, the author's username and email (set during customization).

You can use "git log" to list the commit data; or "git log --stat" to view the file statistics:

  1. $ git log
  2. commit 858f3e71b95271ea320d45b69f44dc55cf1ff794
  3. Author: username <email>
  4. Date: Thu Nov 29 13:31:32 2012 +0800
  5. First commit
  6.  
  7. $ git log --stat
  8. commit 858f3e71b95271ea320d45b69f44dc55cf1ff794
  9. Author: username <email>
  10. Date: Thu Nov 29 13:31:32 2012 +0800
  11. First commit
  12. Hello.java | 6 ++++++
  13. README.md | 2 ++
  14. 2 files changed, 8 insertions(+)

Each commit is identified by a 40-hex-digit SHA-1 hash code. But we typcially use the first 7 hex digits to reference a commit, as highlighted.

To view the commit details, use "git log -p", which lists all the patches (or changes).

  1. $ git log -p
  2. commit 858f3e71b95271ea320d45b69f44dc55cf1ff794
  3. Author: username <email>
  4. Date: Thu Nov 29 13:31:32 2012 +0800
  5. First commit
  6. diff --git a/Hello.java b/Hello.java
  7. new file mode 100644
  8. index 0000000..dc8d4cf
  9. --- /dev/null
  10. +++ b/Hello.java
  11. @@ -0,0 +1,6 @@
  12. +// Hello.java
  13. +public class Hello {
  14. + public static void main(String[] args) {
  15. + System.out.println("Hello, world from GIT!");
  16. + }
  17. +}
  18. diff --git a/README.md b/README.md
  19. new file mode 100644
  20. index 0000000..9565113
  21. --- /dev/null
  22. +++ b/README.md
  23. @@ -0,0 +1,2 @@
  24. +// README.md
  25. +This is the README file for the Hello-world project.

Below are more options of using "git log":

  1. $ git log --oneline
  2. // Display EACH commit in one line.
  3.  
  4. $ git log --author="<author-name-pattern>"
  5. // Display commits by author
  6.  
  7. $ git log <file-pattern>
  8. // Display commits for particular file(s)
  9.  
  10. // EXAMPLES
  11. $ git log --author="Tan Ah Teck" -p Hello.java
  12. // Display commits for file "Hello.java" by a particular author
File Status (git status)

A file could be untracked or tracked.

As mentioned, Git tracks file changes at commits. In Git, changes for a tracked file could be:

  1. unstaged (in Working Tree) - called unstaged changes,
  2. staged (in Staging Area or Index or Cache) - called staged changes, or
  3. committed (in local repo object database).

The files in "working tree" or "staging area" could have status of unmodified, added, modified, deleted, renamed, copied, as reported by "git status".

The "git status" output is divided into 3 sections: "Changes not staged for commit" for the unstaged changes in "working tree", "Changes to be committed" for the staged changes in the "staging area", and "Untracked files". In each section, It lists all the files that have been changed, i,e., files having status other than unmodified.

When a new file is created in the working tree, it is marked as new in working tree and shown as an untracked file. When the file change is staged, it is marked as new (added) in the staging area, and unmodified in working tree. When the file change is committed, it is marked as unmodified in both the working tree and staging area.

When a committed file is modified, it is marked as modified in the working tree and unmodified in the staging area. When the file change is staged, it is marked as modified in the staging area and unmodified in the working tree. When the file change is committed, it is marked as unmodified in both the working tree and staging area.

For example, made some changes to the file "Hello.java", and check the status again:

  1. // Hello.java
  2. public class Hello {
  3. public static void main(String[] args) {
  4. System.out.println("Hello, world from GIT!");
  5. System.out.println("Changes after First commit!");
  6. }
  7. }
  1. $ git status
  2. On branch master
  3. Changes not staged for commit:
  4. (use "git add <file>..." to update what will be committed)
  5. (use "git checkout -- <file>..." to discard changes in working directory)
  6. modified: Hello.java
  7.  
  8. Untracked files:
  9. (use "git add <file>..." to include in what will be committed)
  10. Hello.class
  11. no changes added to commit (use "git add" and/or "git commit -a")

The "Hello.java" is marked as modified in the working tree (under "Changes not staged for commit"), but unmodified in the staging area (not shown in "Changes to be committed").

You can inspect all the unstaged changes using "git diff" command (or "git diff <file>" for the specified file). It shows the file changes in the working tree since the last commit:

  1. $ git diff
  2. diff --git a/Hello.java b/Hello.java
  3. index dc8d4cf..f4a4393 100644
  4. --- a/Hello.java
  5. +++ b/Hello.java
  6. @@ -2,5 +2,6 @@
  7. public class Hello {
  8. public static void main(String[] args) {
  9. System.out.println("Hello, world from GIT!");
  10. + System.out.println("Changes after First commit!");
  11. }
  12. }

The older version (as of last commit) is marked as --- and new one as +++. Each chunk of changes is delimited by "@@ -<old-line-number>,<number-of-lines> +<new-line-number>,<number-of-lines> @@". Added lines are marked as + and deleted as -. In the above output, older version (as of last commit) from line 2 for 5 lines and the modified version from line 2 for 6 lines are compared. One line (marked as +) is added.

Stage the changes of "Hello.java" by issuing the "git add <file>...":

  1. $ git add Hello.java
  2.  
  3. $ git status
  4. On branch master
  5. Changes to be committed:
  6. (use "git reset HEAD <file>..." to unstage)
  7. modified: Hello.java
  8.  
  9. Untracked files:
  10. (use "git add <file>..." to include in what will be committed)
  11. Hello.class

Now, it is marked as modified in the staging area ("Changes to be committed"), but unmodified in the working tree (not shown in "Changes not staged for commit").

Now, the changes have been staged. Issuing an "git diff" to show the unstaged changes results in empty output.

You can inspect the staged change (in the staging area) via "git diff --staged" command:

  1. // List all "unstaged" changes for all files (in the working tree)
  2. $ git diff
  3. // empty output - no unstaged change
  4.  
  5. // List all "staged" changes for all files (in the staging area)
  6. $ git diff --staged
  7. diff --git a/Hello.java b/Hello.java
  8. index dc8d4cf..f4a4393 100644
  9. --- a/Hello.java
  10. +++ b/Hello.java
  11. @@ -2,5 +2,6 @@
  12. public class Hello {
  13. public static void main(String[] args) {
  14. System.out.println("Hello, world from GIT!");
  15. + System.out.println("Changes after First commit!");
  16. }
  17. }
  18. // The "unstaged" changes are now "staged".

Commit ALL staged file changes via "git commit":

  1. $ git commit -m "Second commit"
  2. [master 96efc96] Second commit
  3. 1 file changed, 1 insertion(+)
  4.  
  5. $ git status
  6. On branch master
  7. Untracked files:
  8. (use "git add <file>..." to include in what will be committed)
  9. Hello.class
  10. nothing added to commit but untracked files present (use "git add" to track)

Once the file changes are committed, it is marked as unmodified in the staging area (not shown in "Changes to be committed").

Both "git diff" and "git diff --staged" return empty output, signalling there is no "unstaged" and "staged" changes.

The stage changes are cleared when the changes are committed; while the unstaged changes are cleared when the changes are staged.

Issue "git log" to list all the commits:

  1. $ git log
  2. commit 96efc96f0856846bc495aca2e4ea9f06b38317d1
  3. Author: username <email>
  4. Date: Thu Nov 29 14:09:46 2012 +0800
  5. Second commit
  6.  
  7. commit 858f3e71b95271ea320d45b69f44dc55cf1ff794
  8. Author: username <email>
  9. Date: Thu Nov 29 13:31:32 2012 +0800
  10. First commit

Check the patches for the latest commit via "git log -p -1", with option -n to limit to the last n commit:

  1. $ git log -p -1
  2. commit 96efc96f0856846bc495aca2e4ea9f06b38317d1
  3. Author: username <email>
  4. Date: Thu Nov 29 14:09:46 2012 +0800
  5. Second commit
  6. diff --git a/Hello.java b/Hello.java
  7. index dc8d4cf..ede8979 100644
  8. --- a/Hello.java
  9. +++ b/Hello.java
  10. @@ -2,5 +2,6 @@
  11. public class Hello {
  12. public static void main(String[] args) {
  13. System.out.println("Hello, world from GIT!");
  14. + System.out.println("Changes after First commit!");
  15. }
  16. }

I shall stress again Git tracks the "file changes" at each commit over the previous commit.

The .gitignore File

All the files in the Git directory are either tracked or untracked. To ignore files (such as .class, .o, .exe which could be reproduced from source) from being tracked and remove them from the untracked file list, create a ".gitignore" file in your project directory, which list the files to be ignored, as follows:

  1. # .gitignore
  2.  
  3. # Java class files
  4. *.class
  5.  
  6. # Executable files
  7. *.exe
  8.  
  9. # Object and archive files
  10. # Can use regular expression, e.g., [oa] matches either o or a
  11. *.[oa]
  12.  
  13. # temp sub-directory (ended with a directory separator)
  14. temp/

There should NOT be any trailing comments for filename. You can use regexe for matching the filename/pathname patterns, e.g. [oa] denotes either o or a. You can override the rules by using the inverted pattern (!), e.g., Adding !hello.exe includes the hello.exe although *.exe are excluded.

Now, issue a "git status" command to check the untracked files.

  1. $ git status
  2. On branch master
  3. Untracked files:
  4. (use "git add <file>..." to include in what will be committed)
  5. .gitignore
  6. nothing added to commit but untracked files present (use "git add" to track)

Now, "Hello.class" is not shown in "Untracked files".

Typically, we also track and commit the .gitignore file.

  1. $ git add .gitignore
  2.  
  3. $ git status
  4. On branch master
  5. Changes to be committed:
  6. (use "git reset HEAD <file>..." to unstage)
  7. new file: .gitignore
  8.  
  9. $ git commit -m "Added .gitignore"
  10. [master 711ef4f] Added .gitignore
  11. 1 file changed, 14 insertions(+)
  12. create mode 100644 .gitignore
  13.  
  14. $ git status
  15. On branch master
  16. nothing to commit, working directory clean

3.2  Setting up Remote Repo

  1. Sign up for a GIT host, such as Github https://github.com/signup/free (Unlimited for public projects; fee for private projects); or BitBucket @ https://bitbucket.org/ (Unlimited users for public projects; 5 free users for private projects; Unlimited for Academic Plan); among others.
  2. Login to the GIT host. Create a new remote repo called "test".
  3. On your local repo (let's continue to work on our "hello-git" project), set up the remote repo's name and URL via "git remote add <remote-name> <remote-url>" command.
    By convention, we shall name our remote repo as "origin". You can find the URL of a remote repo from the Git host. The URL may take the form of HTTPS or SSH. Use HTTPS for simplicity.
    1. // Change directory to your local repo's working directory
    2. $ cd /path-to/hello-git
    3.  
    4. // Add a remote repo called "origin" via "git remote add <remote-name> <remote-url>"
    5. // For examples,
    6. $ git remote add origin https://github.com/your-username/test.git // for GitHub
    7. $ git remote add origin https://username@bitbucket.org/your-username/test.git // for Bitbucket

    You can list all the remote names and their corresponding URLs via "git remote -v", for example,

    1. // List all remote names and their corresonding URLs
    2. $ git remote -v
    3. origin https://github.com/your-username/test.git (fetch)
    4. origin https://github.com/your-username/test.git (push)

    Now, you can manage the remote connection, using a simple name instead of the complex URL.

  4. Push the commits from the local repo to the remote repo via "git push -u <remote-name> <local-branch-name>".
    By convention, the main branch of our local repo is called "master" (as seen from the earlier "git status" output). We shall discuss "branch" later.
    1. // Push all commits of the branch "master" to remote repo "origin"
    2. $ git push origin master
    3. Username for 'https://github.com': ******
    4. Password for 'https://your-username@github.com': *******
    5. Counting objects: 10, done.
    6. Delta compression using up to 8 threads.
    7. Compressing objects: 100% (10/10), done.
    8. Writing objects: 100% (10/10), 1.13 KiB | 0 bytes/s, done.
    9. Total 10 (delta 1), reused 0 (delta 0)
    10. To https://github.com/your-username/test.git
    11. * [new branch] master -> master
    12. Branch master set up to track remote branch master from origin.
  5. Login to the GIT host and select the remote repo "test", you shall find all the committed files.
  6. On your local system, make some change (e.g., on "Hello.java"); stage and commit the changes on the local repo; and push it to the remote. This is known as the "Edit/Stage/Commit/Push" cycle.
    1. // Hello.java
    2. public class Hello {
    3. public static void main(String[] args) {
    4. System.out.println("Hello, world from GIT!");
    5. System.out.println("Changes after First commit!");
    6. System.out.println("Changes after Pushing to remote!");
    7. }
    8. }
    1. $ git status
    2. On branch master
    3. Your branch is up-to-date with 'origin/master'.
    4.  
    5. Changes not staged for commit:
    6. (use "git add <file>..." to update what will be committed)
    7. (use "git checkout -- <file>..." to discard changes in working dire
    8. modified: Hello.java
    9. no changes added to commit (use "git add" and/or "git commit -a")
    10.  
    11. // Stage file changes
    12. $ git add *.java
    13.  
    14. $ git status
    15. On branch master
    16. Your branch is up-to-date with 'origin/master'.
    17.  
    18. Changes to be committed:
    19. (use "git reset HEAD <file>..." to unstage)
    20. modified: Hello.java
    21.  
    22. // Commit all staged file changes
    23. $ git commit -m "Third commit"
    24. [master 744307e] Third commit
    25. 1 file changed, 1 insertion(+)
    26.  
    27. // Push the commits on local master branch to remote
    28. $ git push origin master
    29. Username for 'https://github.com': ******
    30. Password for 'https://username@github.com': ******
    31. Counting objects: 5, done.
    32. Delta compression using up to 8 threads.
    33. Compressing objects: 100% (3/3), done.
    34. Writing objects: 100% (3/3), 377 bytes | 0 bytes/s, done.
    35. Total 3 (delta 1), reused 0 (delta 0)
    36. To https://github.com/your-username/test.git
    37. 711ef4f..744307e master -> master

    Again, login to the remote to check the committed files.

3.3  Cloning a Project from a Remote Repo (git clone <remote-url>)

As mentioned earlier, you can start a local GIT repo either running "git init" on your own project, or "git clone <remote-url>" to copy from an existing project.

Anyone having read access to your remote repo can clone your project. You can also clone any project in any public remote repo.

The "git clone <remote-url>" initializes a local repo and copies all files into the working tree. You can find the URL of a remote repo from the Git host.

  1. // SYNTAX
  2. // ======
  3. $ git clone <remote-url>
  4. // <url>: can be https (recommended), ssh or file.
  5. // Clone the project UNDER the current directory
  6. // The name of the "working directory" is the same as the remote project name
  7. $ git clone <remote-url> <working-directory-name>
  8. // Clone UNDER current directory, use the given "working directory" name
  9.  
  10. // EXAMPLES
  11. // ========
  12. // Change directory (cd) to the "parent" directory of the project directory
  13. $ cd path-to-parent-of-the-working-directory
  14.  
  15. // Clone our remote repo "test" into a new working directory called "hello-git-cloned"
  16. $ git clone https://github.com/your-username/test.git hello-git-cloned
  17. Cloning into 'hello-git-cloned'...
  18. remote: Counting objects: 13, done.
  19. remote: Compressing objects: 100% (11/11), done.
  20. remote: Total 13 (delta 2), reused 13 (delta 2)
  21. Unpacking objects: 100% (13/13), done.
  22. Checking connectivity... done.
  23.  
  24. // Verify
  25. $ cd hello-git-cloned
  26.  
  27. $ ls -a
  28. . .. .git .gitignore Hello.java README.md
  29.  
  30. $ git status
  31. On branch master
  32. Your branch is up-to-date with 'origin/master'.
  33. nothing to commit, working directory clean

The "git clone" automatically creates a remote name called "origin" mapped to the cloned remote-URL. You can check via "git remote -v":

  1. // List all the remote names
  2. $ git remote -v
  3. origin https://github.com/your-username/test.git (fetch)
  4. origin https://github.com/your-username/test.git (push)

3.4  Summary of Basic "Edit/Stage/Commit/Push" Cycle

  1. // Edit (Create, Modified, Rename, Delete) files,
  2. // which produces "unstaged" file changes.
  3.  
  4. // Stage file changes, which produces "Staged" file changes
  5. $ git add <file> // for new and modified files
  6. $ git rm <file> // for deleted files
  7. $ git mv <old-file-name> <new-file-name> // for renamed file
  8.  
  9. // Commit (ALL staged file changes)
  10. $ git commit -m "message"
  11.  
  12. // Push
  13. $ git push <remote-name> <local-branch-name>

OR,

  1. // Stage ALL files with changes
  2. $ git add -A // OR, 'git add --all'
  3.  
  4. $ git commit -m "message"
  5. $ git push

OR,

  1. // Add All and Commit in one command
  2. $ git commit -a -m "message"
  3.  
  4. $ git push

3.5  More on Staged and Unstaged Changes

If you modify a file, stage the changes and modify the file again, there will be staged changes and unstaged changes for that file.

For example, let's continue the "hello-git" project. Add one more line to "README.md" and stage the changes:

  1. // README.md
  2. This is the README file for the Hello-world project.
  3. Make some changes and staged.
  1. $ git status
  2. On branch master
  3. Your branch is up-to-date with 'origin/master'.
  4. Changes not staged for commit:
  5. modified: README.md
  6.  
  7. $ git add README.md
  8.  
  9. $ git status
  10. On branch master
  11. Your branch is up-to-date with 'origin/master'.
  12. Changes to be committed:
  13. modified: README.md

Before the changes are committed, suppose we modify the file again:

  1. // README.md
  2. This is the README file for the Hello-world project.
  3. Make some changes and staged.
  4. Make more changes before the previous changes are committed.
  1. $ git status
  2. On branch master
  3. Your branch is up-to-date with 'origin/master'.
  4.  
  5. Changes to be committed:
  6. modified: README.md
  7.  
  8. Changes not staged for commit:
  9. modified: README.md
  10.  
  11. // Now, "README.md" has both unstaged and staged changes.
  12.  
  13. // Show the staged changes
  14. $ git diff --staged
  15. diff --git a/README.md b/README.md
  16. index 9565113..b2e9afb 100644
  17. --- a/README.md
  18. +++ b/README.md
  19. @@ -1,2 +1,3 @@
  20. // README.md
  21. This is the README file for the Hello-world project.
  22. +Make some changes and staged.
  23.  
  24. // Show the unstaged changes
  25. $ git diff
  26. diff --git a/README.md b/README.md
  27. index b2e9afb..ca6622a 100644
  28. --- a/README.md
  29. +++ b/README.md
  30. @@ -1,3 +1,4 @@
  31. // README.md
  32. This is the README file for the Hello-world project.
  33. Make some changes and staged.
  34. +Make more changes before the previous changes are committed.
  35.  
  36. // Stage the changes
  37. $ git add README.md
  38.  
  39. $ git status
  40. On branch master
  41. Your branch is up-to-date with 'origin/master'.
  42.  
  43. Changes to be committed:
  44. modified: README.md
  45.  
  46. // Show staged changes
  47. $ git diff --staged
  48. diff --git a/README.md b/README.md
  49. index 9565113..ca6622a 100644
  50. --- a/README.md
  51. +++ b/README.md
  52. @@ -1,2 +1,4 @@
  53. // README.md
  54. This is the README file for the Hello-world project.
  55. +Make some changes and staged.
  56. +Make more changes before the previous changes are committed.
  57.  
  58. // Commit the staged changes
  59. $ git commit -m "Unstaged vs. Staged Changes"
  60. [master a44199b] Unstaged vs. Staged Changes
  61. 1 file changed, 2 insertions(+), 0 deletion(-)

Take note that the stage changes are cleared when the changes are committed; while the unstaged changes are cleared when the changes are staged.

For convenience, you can also use the "git-gui" tool to view the unstaged and staged changes.

3.6  Git GUI Tools

Git-GUI (Windows)

For convenience, Git provides a GUI tool, called git-gui, which can be used to perform all tasks and view the commit log graphically.

Install "Git-Gui".

To run the git-gui, you can right-click on the project folder and choose "Git Gui"; or launch the Git-bash shell and run "git gui" command.

To view the log, choose "Repository" ⇒ "Visualize master's history", which launches the "gitk". You can view the details of each commit.

You can also view each of the file via "Repository" ⇒ "Browse master's Files" ⇒ Select a file.

Git-gui is bundled with Git. To launch git-gui, right click on the working directory and choose "git gui", or run "git gui" command on the Git-Bash shell.

[TODO]

EGit Plugin for Eclipse

[TODO]

4.  Tagging

Tag (or label) can be used to tag a specific commit as being important, for example, to mark a particular release. The release is often marked in this format: version-number.release-no.modificaton-no (e.g., v1.1.5) or or version-number.release-no.upgrade-no_modificaton-no (e.g., v1.7.0_26).

I recommend that you commit your code and push it to the remote repo as often as needed (e.g., daily), to BACKUP your code. When you code reaches a stable point (in turn of functionality), create a tag to mark the commit, which can then be used for CHECKOUT, if you need to show your code to others.

Listing Tags (git tag)

To list the existing tags, use "git tag" command.

Types of Tags - Lightweight Tags and Annotated Tags

There are two kinds of tags: lightweight tag and annotated tag. Lightweight tag is simply a pointer to a commit. Annotated tag contains annotations (meta-data) and can be digitally signed and verified.

Creating an Annotated Tag (git tag -a <tag-name> -m <message>)

To create an annotated tag at the latest commit, use "git tag -a <tag-name> -m <message>", where -a option specifies annotation tag having meta-data. For example,

  1. $ git tag -a v1.0.0 -m "First production system"
  2.  
  3. // List all tags
  4. $ git tag
  5. v1.0.0
  6.  
  7. // Show tag details
  8. $ git show v1.0.0
  9. // Show the commit point and working tree

To create a tag for an earlier commit, you need to find out the commit's name (first seven character hash code) (via "git log"), and issue "git tag -a <tag-name> -m <message> <commit-name>". For example,

  1. $ git log
  2. ......
  3. commit 7e7cb40a9340691e2b16a041f7185cee5f7ba92e
  4. ......
  5. Commit 3
  6.  
  7. $ git tag -a "v0.9.0" -m "Last pre-production release" 7e7cb40
  8.  
  9. // List all tags
  10. $ git tag
  11. v0.9.0
  12. v1.0.0
  13.  
  14. // Show details of a tag
  15. $ git show v0.9.0
  16. ......

[TODO] Diagram

Creating Lightweight Tags (git tag <tag-name>)

To create a lightweight tag (without meta-data), use "git tag <tag-name>" without the -a option. The lightweight tag stores only the commit hash code.

Signed Tags

You can signed your tags with your private key, with -s option instead of -a.

To verify a signed tag, use -v option and provide the signer's public key.

[TODO] Example

Pushing to Remote Repo

By default, Git does not push tags (and branches) to remote repo. You need to push them explicitly, via "git push origin <tag-name>" for a particular tag or "git push origin --tags" for all the tags.

5.  Branching/Merging

5.1  Git's Data Structures

Git has two primary data structures:

  1. an immutable, append-only object database (or local repo) that stores all the commits and file contents;
  2. a mutable staging area (or index, or cache) that caches the staged information.

The staging area serves as the connection between object database and working tree (as shown in the storage model diagram). It serves to avoid volatility, and allows you to stage ALL the file changes before issuing a commit, instead of committing individual file change. Changes to files that have been explicitly added to the index (staging area) via "git add <file>" are called staged changes. Changes that have not been added are called unstaged changes. Staged and unstaged changes can co-exist. Performing a commit copies the statged changes into object database (local repo) and clears the index. The unstaged changes remain in working tree.

The object database contains these objects:

  • Each version of a file is represented by a blob (binary large object - a file that can contain any data: binaries or characters). A blob holds the file data only, without any metadata - not even the filename.
  • A snapshot of the working tree is represented by a tree object, which links the blobs and sub-trees for sub-directories.
  • A commit object points to a tree object, i.e., the snapshot of the working tree at the point the commit was created. It holds metadata such as timestamp, log message, author's and committer's username and email. It also references its parent commit(s), except the root commit which has no parent. A normal commit has one parent; a merge commit could have multiple parents. A commit, where new branch is created, has more than one children. By referencing through the chain of parent commit(s), you can discover the history of the project.

Each object is identified (or named) by a 160-bit (or 40 hex-digit) SHA-1 hash value of its contents (i.e., a content-addressable name). Any tiny change to the contents produces a different hash value, resulted in a different object. Typically, we use the first 7 hex-digit prefix to refer to an object, as long as there is no ambiguity.

There are two ways to refer to a particular commit: via a branch or a tag.

  • A branch is a mobile reference of commit. It moves forward whenever commit is made on that branch.
  • A tag (like a label) marks a particular commit. Tag is often used for marking the releases.

5.2  Branching

Branching allows you and your team members to work on different aspects of the software concurrently (on so-called feature branches), and merge into the master branch as and when they completes. Branching is the most important feature in a concurrent version control system.

A branch in Git is a lightweight movable pointer to one of the commits. For the initial commit, Git assigns the default branch name called master and sets the master branch pointer at the initial commit. As you make further commits on the master branch, the master branch pointer move forward accordingly. Git also uses a special pointer called HEAD to keep track of the branch that you are currently working on. The HEAD always refers to the latest commit on the current branch. Whenever you switch branch, the HEAD also switches to the latest commit on the branch switched.

Example

For example, let's create a Git-managed project called git_branch_test with only the a single-line README.md file:

  1. This is the README. My email is xxx@somewhere.com
  1. $ git init
  2. $ git add README.md
  3. $ git commit -m "Commit 1"
  4.  
  5. // Append a line in README.md: This line is added after Commit 1
  6. $ git status
  7. $ git add README.md
  8. $ git commit -m "Commit 2"
  9.  
  10. // Append a line in README.md: This line is added after Commit 2
  11. $ git status
  12. $ git add README.md
  13. $ git commit -m "Commit 3"
  14.  
  15. // Show all the commits (oneline each)
  16. $ git log --oneline
  17. 44fdf4c Commit 3
  18. 51f6827 Commit 2
  19. fbed70e Commit 1

Creating a new Branch (git branch <branch-name>)

You can create a new branch via "git branch <branch-name>" command. When you create a new branch (says devel, or development), Git creates a new branch pointer for the branch devel, pointing initially at the latest commit on the current branch master.

  1. $ git branch devel

Take note that when you create a new branch, the HEAD pointer is still pointing at the current branch.

Branch Names Convention
  • master branch: the production branch with tags for the various releases.
  • development (or next or devel) branch: developmental branch, to be merged into master if and when completes.
  • topics branch: a short-live branch for a specific topics, such as introducing a feature (for the devel branch) or fixing a bug (for the master branch).
Switching to a Branch (git checkout <branch-name>)

Git uses a special pointer called HEAD to keep track of the branch that you are working on. The "git branch <branch-name>" command simply create a branch, but does not switch to the new branch. To switch to a branch, use "git checkout <branch-name>" command. The HEAD pointer will be pointing at the switched branch (e.g., devel).

  1. $ git checkout devel
  2. Switched to branch 'devel'

Alternatively, you can use "git checkout -b <branch-name>" to create a new branch and switch into the new branch.

If you switch to a branch and make changes and commit. The HEAD pointer moves forward in that branch.

  1. // Append a line in README.md: This line is added on devel branch after Commit 3
  2. $ git status // NOTE "On branch devel"
  3. $ git add README.md
  4. $ git commit -m "Commit 4"
  5. [devel c9b88d9] Commit 4

You can switch back to the master branch via "git checkout master". The HEAD pointer moves back to the last commit of the master branch, and the working directory is rewinded back to the latest commit on the master branch.

  1. $ git checkout master
  2. Switched to branch 'master'
  3. // Check the content of the README.md, which is reminded back to Commit 3

If you continue to work on the master branch and commit, the HEAD pointer moves forward on the master branch. The two branches now diverge.

  1. // Append a line in README.md: This line is added on master branch after Commit 4
  2. $ git status // NOTE "On branch master"
  3. $ git add README.md
  4. $ git commit -m "Commit 5"
  5. [master 6464eb8] Commit 5

If you check out the devel branch, the file contents will be rewinded back to Commit-4.

  1. $ git checkout devel
  2. // Check file contents

5.3  Merging Two Branches (git merge <branch-name>)

To merge two branches, says master and devel, check out the first branch, e,g, master, (via "git checkout <branch-name>") and merge with another branch, e.g., devel, via command "git merge <branch-name>".

Fast-Forward Linear Merge

If the branch to be merged is a direct descendant, Git performs fast forward by moving the HEAD pointer forward. For example, suppose that you are currently working on the devel branch at commit-4, and the master branch's latest commit is at commit-3:

  1. $ git checkout master
  2.  
  3. // Let discard the Commit-5 totally and rewind to commit-3 on master branch
  4. // This is solely for illustration!!! Do this with great care!!!
  5. $ git reset --hard HEAD~1
  6. HEAD is now at 7e7cb40 Commit 3
  7. // HEAD~1 moves the HEAD pointer back by one commit (-1)
  8. // --hard also resets the working tree
  9.  
  10. // Check the file contents
  11.  
  12. $ git merge devel
  13. Updating 7e7cb40..4848c7b
  14. Fast-forward
  15. README.md | 1 +
  16. 1 file changed, 1 insertion(+)
  17.  
  18. // Check the file contents

Take note that no new commit is created.

3-Way Merge

If the two branches are diverged, git automatically searches for the common ancestor commit and performs a 3-way merge. If there is no conflict, a new commit will be created.

If git detects a conflict, it will pause the merge and issue a merge conflict and ask you to resolve the conflict manually. The file is marked as unmerged. You can issue "git status" to check the unmerged files, study the details of the conflict, and decide which way to resolve the conflict. Once the conflict is resolve, stage the file (via "git add <file>"). Finally, run a "git commit" to finalize the 3-way merge (the same Edit/Stage/Commit cycle).

  1. $ git checkout master
  2. // undo the Commit-4, back to Commit-3
  3. $ git reset --hard HEAD~1
  4. HEAD is now at 7e7cb40 Commit 3
  5.  
  6. // Change the email to abc@abc.com
  7. $ git add README.md
  8. $ git commit -m "Commit 5"
  9.  
  10. $ git checkout devel
  11. // undo the Commit-4, back to Commit-3
  12. $ git reset --hard HEAD~1
  13. // Change the email to xyz@xyz.com to trigger conflict
  14. $ git add README.md
  15. $ git commit -m "Commit 4"
  16.  
  17. // Let's do a 3-way merge with conflict
  18. $ git checkout master
  19. $ git merge devel
  20. Auto-merging README.md
  21. CONFLICT (content): Merge conflict in README.md
  22. Automatic merge failed; fix conflicts and then commit the result.
  23.  
  24. $ git status
  25. # On branch master
  26. # You have unmerged paths.
  27. # (fix conflicts and run "git commit")
  28. #
  29. # Unmerged paths:
  30. # (use "git add <file>..." to mark resolution)
  31. # both modified: README.md
  32. no changes added to commit (use "git add" and/or "git commit -a")

The conflict file is marked as follows (in "git status"):

  1. <<<<<<< HEAD
  2. This is the README. My email is abc@abc.com
  3. =======
  4. This is the README. My email is xyz@xyz.com
  5. >>>>>>> devel
  6. This line is added after Commit 1
  7. This line is added after Commit 2

You need to manually decide which way to take, or you could discard both by setting the email to zzz@nowhere.com.

  1. $ git add README.md
  2. $ git commit -m "Commit 6"

Take note that In a 3-way merge, a new commit will be created in the process (unlike fast-forward merge).

Deleting a Merged Branch (git branch -d <branch-name>)

The merged branch (e.g., devel) is no longer needed. You can delete it via "git branch -d <branch-name>".

  1. $ git branch -d devel
  2. Deleted branch devel (was a20f002).
  3.  
  4. // Create the development branch again at the latest commit
  5. $ git branch devel

5.4  Rebasing Branch (git rebase)

The primary purpose for rebasing is to maintain a linear project history. For example, if you checkout a devel branch and work on commit-5 and commit-6, instead of doing a 3-way merge into the master branch and subsequently remove the devel branch, you can rebase the commit-5 and commit-6, on commit-4, and perform a linear forward merge to maintain all the project history. New commits (7 and 8) will be created for the rebased commit (5 and 6).

The syntax is:

  1. // SYNTAX
  2. $ git rebase <base-name>
  3. // <base-name> could be any kind of commit reference
  4. // (such as an commit-name, a branch name, a tag,
  5. // or a relative reference to HEAD).

Examples:

  1. // Start a new feature branch from the current master
  2. $ git checkout -b feature master
  3. // Edit/Stage/Commit changes to feature branch
  4.  
  5. // Need to work on a fix on the master
  6. $ git checkout -b hotfix master
  7. // Edit/Stage/Commit changes to hotfix branch
  8. // Merge hotfix into master
  9. $ git checkout master
  10. $ git merge hotfix
  11. // Delete hotfix branch
  12. $ git branch -d hotfix
  13.  
  14. // Rebase feature branch on master branch
  15. // to maintain a linear history
  16. $ git checkout feature
  17. $ git rebase master
  18. // Now, linear merge
  19. $ git checkout master
  20. $ git merge feature

5.5  Amend the Last Commit (git commit --amend)

If you make a commit but want to change the commit message or adding more changes, you may amend the recent commit (instead of creating new commit) via command "git commit --amend"):

  1. $ git commit --amend -m "message"

For example,

  1. // Do a commit
  2. $ git commit -m "added login menu"
  3.  
  4. // Realize that you have not staged some files.
  5. // Amend the commit
  6. $ git add morefile
  7. $ git commit --amend
  8. // You could modify the commit message here

5.6  More on "git checkout" and Detached HEAD

"git checkout" can be used to checkout a branch, a commit, or files. The syntaxes are:

  1. $ git checkout <branch-name>
  2. $ git checkout <commit-name>
  3. $ git checkout <commit-name> <filename>

When you checkout a commit, Git switches into so-called "Detached HEAD" state, i.e., the HEAD detached from the tip of a branch. Suppose that you continue to work on the detached HEAD on commit-5, and wish to merge the commit-5 back to master. You checkout the master branch, but there is no branch name for your to reference the commit-5!!!

In Summary, you can use "git checkout <commit-name>" to inspect a commit. BUT you should always work on a branch, NOT on a detached HEAD.

5.7  More on "git reset" and "git reset --hard"

[TODO] examples and diagram

  1. $ git reset <file>
  2. // Unstage the changes of <file> from staging area,
  3. // not affecting the working tree.
  4.  
  5. $ git reset
  6. // Reset the staging area
  7. // Remove all changes (of all files) from staging area,
  8. // not affecting the working tree.
  9.  
  10. $ git reset --hard
  11. // Reset the staging area and working tree to match the
  12. // recent commit (i.e., discard all changes since the
  13. // last commit).
  14.  
  15. $ git reset <commit-name>
  16. // Move the HEAD of current branch to the given commit,
  17. // not affecting the working tree.
  18.  
  19. $ git reset --hard <commit-name>
  20. // Reset both staging area and working tree to the given
  21. // commit, i.e., discard all changes after that commit.

[TODO] Diagram

[TODO] --soft option

5.8  git revert <commit-name>

The "git revert" undoes a commit. But, instead of removing the commit from the project history, it undos the changes introduced by the commit and appends a new commit with the resulting content. This prevents Git from losing history. "git revert" is a safer way comparing with "git reset".

  1. // SYNTAX
  2. $ git revert <commit-name>
  3.  
  4. // EXAMPLE
  5. [TODO] example and diagram

5.9  Summary of Work Flows

Setting up GIT and "Edit/Stage/Commit/Push" Cycle

Step 1: Install GIT.

  • For Windows and Mac, download the installer from http://git-scm.com/downloads and run the downloaded installer.
  • For Ubuntu, issue command "sudo apt-get install git".

For Windows, use "git-bash" command shell provided by Windows installer to issue command. For Mac/Ubuntu, use "Terminal".

Step 2: Configuring GIT:

  1. // Setup your username and email to be used in labeling commits
  2. $ git config --global user.email "your-email@yourmail.com"
  3. $ git config --global user.name "your-name"

Step 3: Set up GIT repo for a project. For example, we have a project called "olas1.1" located at "/usr/local/olas/olas1.1".

  1. $ cd /usr/local/olas/olas1.1
  2.  
  3. // Initialize the GIT repo
  4. $ git init
  5.  
  6. $ ls -al
  7. // Check for ".git" directory

Create a "README.md" (or "README.textile" if you are using Eclipse's WikiText in "textile" markup) under your project directory to describe the project.

Step 4: Start "Edit/Stage/Commit/Push" cycles.

Create/Modify files. Stage files into the staging area via "git add <file>".

  1. // Check the status
  2. $ git status
  3. ......
  4.  
  5. // Add files into repo
  6. $ git add README.md
  7. $ git add www
  8. ......
  9.  
  10. // Check the status
  11. $ git status
  12. ......

Step 5: Create a ".gitignore" (in the project base directory) to exclude folders/files from being tracked by GIT. Check your "git status" output to decide which folders/files to be ignored.

For example,

  1. # ignore files and directories beginning with dot
  2. .*
  3.  
  4. # ignore directories beginning with dot (a directory ends with a slash)
  5. .*/
  6.  
  7. # ignore these files and directories
  8. www/test/
  9. www/.*
  10. www/.*/

The trailing slash indicate directory (and its sub-directories and files).

If you want the ".gitignore" to be tracked (which is in the ignore list):

  1. $ git add -f .gitignore
  2. // -f to override the .gitignore

Step 6: Commit.

  1. $ git status
  2. ......
  3.  
  4. // Commit with a message
  5. $ git commit -m "Initial Commit"
  6. ......
  7.  
  8. $ git status
  9. ......

Step 7: Push to the Remote Repo (for backup, version control, and collaboration).

You need to first create a repo (says olas) in a remote GIT host, such as GitHub or BitBucket. Take note of the remote repo URL, e.g., https://username@hostname.org/username/olas.git.

  1. $ cd /path-to/local-repo
  2.  
  3. // Add a remote repo name called "origin" mapped to the remote URL
  4. $ git remote add origin https://hostname/username/olas.git
  5.  
  6. // Push the "master" branch to the remote "origin"
  7. // "master" is the default branch name of your local repo after init.
  8. $ git push origin master

Check the remote repo for the files committed.

Step 8: Work on the source files, make changes, commit and push to remote repo.

  1. // Check the files modified
  2. $ git status
  3. ......
  4.  
  5. // Stage for commit the modified files
  6. $ git add ....
  7. ......
  8.  
  9. // Commit (with a message)
  10. $ git commit -m "commit-message"
  11.  
  12. // Push to remote repo
  13. $ git push origin master

Step 9: Create a "tag" (for version number).

  1. // Tag a version number to the current commit
  2. $ git tag -a v1.1 -m "Version 1.1"
  3. // -a to create an annotated tag, -m to provide a message
  4.  
  5. // Display all tags
  6. $ git tag
  7. ......
  8.  
  9. // Push the tags to remote repo
  10. // ("git push -u origin master" does not push the tags)
  11. $ git push origin --tags
Branch and Merge Workflow

It is a good practice to freeze the "master" branch for production; and work on a development branch (says "devel") instead. You may often spawn a branch to fix a bug in the production.

  1. // Create a branch called "devel" and checkout.
  2. // The "devel" is initially synchronized with the "master" branch.
  3. $ git checkout -b devel
  4. // same as:
  5. // $ git branch devel
  6. // $ git checkout
  7.  
  8. // Edit/Stage/Commit
  9. $ git add <file>
  10. $ git commit -m "commit-message"
  11.  
  12. // To merge the "devel" into the production "master" branch
  13. $ git checkout master
  14. $ git merge devel
  15.  
  16. // Push both branches to remote repo
  17. $ git push origin master devel
  18.  
  19. // Checkout the "devel" branch and continue...
  20. $ git checkout devel
  21. // Edit/Stage/Commit/Push
  22.  
  23. // Need to fix a bug in production (in "master" branch)
  24. $ git checkout master
  25. // Spawn a "fix" branch to fix the bug, and merge with the "master" branch
  26.  
  27. // To remove the "devel" branch (if the branch is out-of-sync)
  28. $ git branch -d devel
  29. // To re-create the "devel" branch
  30. $ git checkout -b devel

5.10  Viewing the Commit Graph (gitk)

You can use the "git-gui" "gitk" tool to view the commit graph.

To run the git-gui, you can right-click on the project folder and choose "Git Gui"; or launch the Git-bash shell and run "git gui" command.

To view the commit graph, choose "Repository" ⇒ "Visualize master's history", which launches the "gitk". You can view the details of each commit.

6.  Collaboration

Reference: https://www.atlassian.com/git/tutorials/making-a-pull-request/how-it-works.

6.1  Synchronizing Remote and Local: Fetch/Merge, Pull and Push

Setup up a remote repo (revision)

As described earlier, you can use "git remote" command to set up a "remote name", mapped to the URL of a remote repo.

  1. // Add a new "remote name" maps to the URL of a remote repo
  2. $ git remote add <remote-name> <remote-url>
  3. // For example,
  4. $ git remote add origin https://hostname/username/project-name.git
  5. // Define a new remote name "origin" mapping to the given URL
  6.  
  7. // List all the remote names
  8. $ git remote -v
  9.  
  10. // Delete a remote name
  11. $ git remote rm <remote-name>
  12.  
  13. // Rename a remote name
  14. $ git remote rename <old-remote-name> <new-remote-name>
Cloning a Remote Repo (revision)
  1. $ git clone <remote-url>
  2. // Init a GIT local repo and copy all objects from the remote repo
  3. $ git clone <remote-url> <working-directory-name>
  4. // Use the working-directory-name instead of default to project name

Whenever you clone a remote repo using command "git clone <remote-url>", a remote name called "origin" is automatically added and mapped to <remote-url>.

[TODO] Diagram

Fetch/Merge Changes from remote (git fetch/merge)

The "git fetch" command imports commits from a remote repo to your local repo, without updating your local working tree. This gives you a chance to review changes before updating (merging into) your working tree. The fetched objects are stored in remote branches, that are differentiated from the local branches.

  1. $ cd /path-to/working-directory
  2.  
  3. $ git fetch <remote-name>
  4. // Fetch ALL branches from the remote repo to your local repo
  5.  
  6. $ git fetch <remote-name> <branch-name>
  7. // Fetch the specific branch from the remote repo to your local repo
  8.  
  9. // List the local branches
  10. $ git branch
  11. * master
  12. devel
  13. // * indicates current branch
  14.  
  15. // List the remote branches
  16. $ git branch -r
  17. origin/master
  18. origin/devel
  19.  
  20. // You can checkout a remote branch to inspect the files/commits.
  21. // But this put you into "Detached HEAD" state, which prevent you
  22. // from updating the remote branch.
  23.  
  24. // You can merge the fetched changes into local repo
  25. $ git checkout master
  26. // Switch to "master" branch of local repo
  27. $ git merge origin/master
  28. // Merge the fetched changes from stored remote branch to local

[TODO] Diagram

git pull

As a short hand, "git pull" combines "git fetch" and "git merge" into one command, for convenience.

  1. $ git pull <remote-name>
  2. // Fetch the remote's copy of the current branch and merge it
  3. // into the local repo immediately, i.e., update the working tree
  4.  
  5. // Same as
  6. $ git fetch <remote-name> <current-branch-name>
  7. $ git merge <remote-name> <current-branch-name>
  8.  
  9. $ git pull --rebase <remote-name>
  10. // linearize local changes after the remote branch.

The "git pull" is an easy way to synchronize your local repo with origin's (or upstream) changes (for a specific branch).

[TODO] Diagram

Pushing to Remote Repo (revision)

The "git push <remote-name> <branch-name>" is the counterpart of "git fetch", which exports commits from local repo to remote repo.

  1. $ git push <remote-name> <branch-name>
  2. // Push the specific branch of the local repo
  3.  
  4. $ git push <remote-name> --all
  5. // Push all branches of the local repo
  6.  
  7. $ git push <remote-name> --tag
  8. // Push all tags
  9. // "git push" does not push tags
  10.  
  11. $ git push -u <remote-name> <branch-name>
  12. // Save the remote-name and branch-name as the
  13. // reference (or current) remote-name and branch-name.
  14. // Subsequent "git push" without argument will use these references.

[TODO] Diagram

6.2  "Fork" and "Pull Request"

"Fork" and "Pull Request" are features provided by GIT hosts (such as GitHub and BitBucket):

  • Pushing "Fork" button to copy a project from an account (e.g., project maintainer) to your own personal account. [TODO] diagram
  • Pushing "Pull Request" button to notify other developers (e.g., project maintainer or the entire project team) to review your changes. If accepted, the project maintainer can pull and apply the changes. A pull request shall provide the source's repo name, source's branch name, destination's repo name and destination's branch name.

6.3  Feature-Branch Workflow for Shared Repo

Feature-Branch workflow is more prevalent with small teams on private projects. Everyone in the team is granted push access to a single shared remote repository and feature (or topic) branches are used to isolate changes made by the team members.

The project maintainer starts the "master" branch on the shared remote repo. All developers clone the "master" branch into their local repos. Each developer starts a feature branch (e.g., "user1-featureX") to work on a feature. Once completed (or even work-in-progress), he files a "pull request" to initiate a review for his feature. All developers can provide comments and suggestions. Once accepted, the project maintainer can then merge the feature branch into the "master" branch.

The steps are:

  1. Mark, the project maintainer, starts the project by pushing to the shared remote repo's "master" branch.
  2. Carol, a contributor, clones the project into her local repo, via:
    1. // Carol:
    2. $ cd parent-directory-of-the-working-directory
    3. $ git clone https://hostname/path-to/project-name.git
    4. // Create a remote-name "origin" (default), branch "master"
    5. // on her local repo
  3. Carol starts a feature branch (says "carol-feature") under the "master" branch to work on a new feature, via:
    1. // Carol:
    2. $ git checkout -b carol-feature master
    3. // Create a new branch "carol-feature" under "master" branch
    4. // and switch to the new branch
    5.  
    6. // Edit/Stage/Commit/Push cycles on carol-feature branch
    7. $ git status
    8. $ git add <file>
    9. $ git commit -m <message>
    10. $ git push origin carol-feature
    11.  
    12. // Repeat until done
  4. Carol completes the new feature. She files a "pull request" (by pushing the "pull request" button on the Git host) to notify the rest of the team members.
  5. Mark, the project maintainer, or anyone in the team, can comment on Carol's feature. Carol can re-work on the feature, if necessary, and pushes all subsequent commits under her feature branch.
  6. Once the feature is accepted, Mark, or anyone in the team (including Carol), performs a merge to apply the feature branch into the "master" branch:
    1. // Mark, or Anyone:
    2. $ git checkout master
    3. // Switch to the "master" branch of the local repo
    4. $ git pull origin master
    5. // Fetch and merge the latest changes on local's "master" branch,
    6. // if any (i.e., synchronize)
    7. $ git pull origin carol-feature
    8. // Fetch and merge carol-feature branch on local's "master" branch
    9. $ git push origin master
    10. // Update the shared remote repo
  7. Everyone can update their local repo, via:
    1. // Everyone:
    2. $ git checkout master
    3. // Switch to the "master" branch of the local repo
    4. $ git pull origin master
    5. // Fetch and merge the latest changes on local "master" branch

[TODO] Diagram

6.4  Forking Workflow

In Forking workflow, Instead of using a common shared remote repo, each developer forks the project to his own personal account on the remote host. He then works on his feature (preferably in a feature branch). Once completed, he files a "pull request" to notify the maintainer to review his changes, and if accepted, merge the changes.

Forking workflow is applicable to developers working in small teams and to a third-party developer contributing to an open source project.

The steps are:

  1. Mark, the project maintainer, pushes the project from his local repo ("master" branch) to a remote Git host. He permits "read" access by contributors.
  2. Carol, a contributor, gotos Mark's repo, forks the project (by pushing the fork button). "Forking" copies the project to Carol's own personal account on the same Git host.
  3. Carol then clones the project from her forked repo into her local repo, via:
    1. // Carol:
    2. $ cd parent-directory-of-the-working-directory
    3. $ git clone https://hostname/carol/project-name.git
    4. // Create a remote name "origin" automatically
    5. // Copy the "master" branch
  4. When a fork is cloned, Git creates a remote-name called origin that points to the fork, not the original repo it was forked from. To keep track of the original repo, Carol creates a remote name called "upstream" and pulls (fetches and merges) all new changes:
    1. // Carol:
    2. $ cd carol-local-repo-of-the-fork
    3. $ git remote add upstream https://hostname/mark/project-name.git
    4. // Create a remote-name "upstream" pointing to the original remote repo
    5.  
    6. $ git remote -v
    7. // List all the remote names and URLs
    8. // origin: mapped to Carol's forked remote repo
    9. // upstream: mapped to Mark's original remote repo
    10.  
    11. $ git pull upstream master
    12. // Fetch and merge all changes from the original remote repo to local repo
    13. // for the "master" branch
  5. Now, Carol can make changes on her local repo (on a new branch), stage and commit the changes, and pushes them to her forked remote repo (so called Edit/Stage/Commit/Push cycles):
    1. // Carol:
    2. $ git checkout -b carol-feature master
    3. // Create a new branch called "carol-feature" under "master"
    4. // and switch to the new branch
    5.  
    6. // Edit/Stage/Commit/Push cycles on "carol-feature" branch
    7. $ git status
    8. $ git add <file>
    9. $ git commit -m <message>
    10. $ git push origin carol-feature
    11.  
    12. // Repeat until done
  6. Carol files a pull request to Mark (the project maintainer) by pushing the pull-request button. She needs to specify her forked remote repo-name, her branch name (carol-feature), Mark's remote repo-name, and Mark's branch name (master).
  7. Mark opens the pull request (in pull request tab), reviews the change, and decides whether to accept the changes. Mark can ask Carol to re-work on the feature. Carol repeats the Edit/Stage/Commit/Push cycles.
    If Mark decides to accept the changes, he pushes the "Merge" button to merge Carol's contribution to his master branch on the remote repo.
    [If there is no "Merge" button] Mark needs to do the following:
    1. // Mark:
    2. $ git checkout master
    3. // Checkout the "master" branch of the local repo
    4.  
    5. $ git remote add carol git://hostname/carol/project-name.git
    6. // Add a new remote pointing to the Carol's forked remote repo
    7.  
    8. $ git pull carol carol-feature
    9. // Fetch and merge the changes into local repo's master branch
    10.  
    11. $ git push origin master
    12. // Push the update to the Mark's original remote repo
  8. All contributors (including Mark and Carol) shall regularly synchronize their local repo by fetch/merge with Mark's master branch.
    1. // Carol (and everyone):
    2. $ git checkout master
    3. // Switch to the "master" branch of the local repo
    4. $ git pull upstream master
    5. // Fetch and merge the latest changes on "master" branch from
    6. // original remote repo to his local repo

6.5  Other Workflows

There are other workflows such as "Centralized Workflow" and "GitFlow Workflow". Read "https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow".

7.  Miscellaneous and How-To

Stage and Commit (git commit -a -m <message>)

You can skip the staging (i.e., the "git add <file>...") and commit allchanges in the working tree via "git commit -a -m <message>" with -a (or --all) option.

Stage all changes (git add -A)

You can use "git add -A" to stages all changes in the working tree to the staging area.

Unstage a Staged file (git rm --cached <file> / git reset head <file>)

Recall that you can use "git add <file>" to stage new files or modified files into the staging area.

To unstage a staged new file, use "git rm --cached <file>".

To unstage a staged modified file, use "git reset head <file>".

Unmodified a modified file (git checkout -- <file>)

After a commit, you may have modified some files. You can discard the changes by checking out the last commit via "git checkout -- <file>".

How to Amend the Last Commit (git commit --amend)

If you make a commit but want to change the commit message:

  1. $ git commit --amend -m "message"

If you make a commit but realize that you have not staged some file changes, you can also do it with --amend:

  1. $ git add morefile
  2. $ git commit --amend

You can also make some changes to working tree, stage, and amend the last commit

  1. // Edit morefile (make changes)
  2. $ git add morefile
  3. $ git commit --amend
How to Undo the Previous Commit(s) (git reset)

To undo previous commit(s):

  1. // Reset the HEAD to the previous commit
  2. // --soft to keep the working tree and index
  3. $ git reset --soft HEAD~1 // Windows
  4. $ git reset --soft HEAD^ // Unix
  5.  
  6. // Make changes
  7. ......
  8.  
  9. // Stage
  10. $ git add ......
  11. // Commit
  12. $ git commit -c ORIG_HEAD

The "git reset --hard HEAD~1" moves the HEAD to the previous commit, restore the working tree and discard the index (i.e., discard all change after the previous commit). Instead of HEAD~n, you can also specify the commit hash code.

The "git rest HEAD~1" with default --mixed moves the HEAD to the previous commit, keep the working tree and discard the index

The "git reset --soft HEAD~1" moves the HEAD to the previous commit, keep the working tree and the index (i.e., keep all changes after the previous commit).

[TODO] Examples, diagrams and "git status" outputs.

For a public repo, you probably need to make another commit and push the commit to the public repo, or ...

Relative Commit Names

A commit is uniquely and absolutely named using a 160-bit (40-hex-digit) SHA-1 hash code of its contents. You can always refer to a commit via its hash value or abbreviated hash value (such as the first 7 hex-digit) if there is no ambiguity.

You can also refer to a commit relatively, e.g., master~1 (Windows), master^ (may not work in Windows), master^1 refers to the previous (parent) commit on the master branch; master~2, master^^ refers to the previous of the previous (grandparent) commit, and etc. If a commit has multiple parents (e.g., due to merging of branches), ^1 refers to the first parent, ^2 refers to the second parent, and so on.

REFERENCES & RESOURCES

  1. GIT mother site @ http://git-scm.com and GIT Documentation @ http://git-scm.com/doc.
  2. Git User's Manual @ http://www.kernel.org/pub/software/scm/git/docs/user-manual.html.
  3. Git Hosts: GitHub @ https://github.com, Bitbucket @ https://bitbucket.org.
  4. Git Tutorials @ https://www.atlassian.com/git/tutorials.
  5. Bitbucket Documentation Home @ https://confluence.atlassian.com/display/BITBUCKET/Bitbucket+Documentation+Home.
  6. Bitbucket 101 @ https://confluence.atlassian.com/display/BITBUCKET/Bitbucket+101.
  7. Jon Loeliger and Matthew McCullough, "Version Control with Git", 2nd Eds, O'reilly, 2012.
  8. Scott Chacon, "Pro Git", Apress, 2009.

How to get started with GIT and work with GIT Remote Repo的更多相关文章

  1. git之win安装git和环境配置及常用命令总结

    12.windowns安装git和环境变量配置 11.git之常见命令总结 ===== 12.windowns安装git和环境变量配置 ; 转自 https://wuzhuti.cn/2385.htm ...

  2. git diff 生成patch, git apply patch 打补丁方法说明,以及分支管理的简单操作。

    git diff 简易操作说明 先git log 查看commit ID, 记录你想要打的补丁的ID 比如说: git log commit 4ff35d800fa62123a28b7bda2a04e ...

  3. git: fatal: Not a git repository (or any of the parent directories): .git

    在看书 FlaskWeb开发:基于Python的Web应用开发实战 时,下载完源码后 git clone https://github.com/miguelgrinberg/flasky.git 试着 ...

  4. 深入理解git,从研究git目录开始

    转发学习的啦. 似乎很少有人在读某个git快速教程的时候会说:“这个关于git的快速教程太酷了!读完了用起git来超级舒服,并且我一点也不怕自己会破坏什么东西.” 对git的初学者来说,刚接触git时 ...

  5. Git详解之三 Git分支

    相关文档 — 更多 Git 基础培训.ppt GIT 使用经验.ppt GIT 介绍.pptx GIT 分支管理是一门艺术.docx Eclipse上GIT插件EGIT使用手册.docx git/gi ...

  6. [git]添加项目到git

    写在前面 一直在想把代码托管到git上面,一直没有去研究,最近发现自己写的demo,好多都找不到了,实在是没办法了,耐下心研究了下git.这里通过添加了自己做的demo,算是也是学习下git的操作吧. ...

  7. git使用命令, 特别:git checkout -b a 与 git branch a区别

    摘自: https://my.oschina.net/u/587974/blog/74341 创建分支: $ git branch mybranch 切换分支: $ git checkout mybr ...

  8. 将在本地创建的Git仓库push到Git@OSC

    引用自:http://my.oschina.net/flan/blog/162189 在使用git 处理对android的修改的过程之中总结的.但不完善 Git push $ git push ori ...

  9. 转 git使用命令, 特别:git checkout -b a 与 git branch a区别

    创建分支: $ git branch mybranch 切换分支: $ git checkout mybranch 创建并切换分支: $ git checkout -b mybranch 更新mast ...

随机推荐

  1. 【三十】php之PDO抽象层

    1.PDO介绍(php data object) PHP 数据对象 (PDO) 扩展为PHP访问数据库定义了一个轻量级的一致接口. PDO 提供了一个数据访问抽象层,这意味着,不管使用哪种数据库,都可 ...

  2. js测试地址

    很多时候,想写js测试代码,比如在学习的时候.看书敲代码,每次打开VS还是很麻烦的.特别是需要加载一些库的时候. 此时有个工具可以解决: https://jsfiddle.net/ 也是在别人的博客里 ...

  3. C# 获取当前方法的名称空间、类名和方法名称

    1.(new StackTrace()).GetFrame(1) // 0为本身的方法:1为调用方法2.(new StackTrace()).GetFrame(1).GetMethod().Name; ...

  4. b9934107349625014ec251e1333d73a8 这个代码是mad5值

    Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛使用的一种散列函数,用以提供消息的完整性保护.该算法的文件号为RFC 1321(R.Rives ...

  5. 开发中使用UEditor编辑器的注意事项

    最近在一个刚结束的一个项目中使用到了UEditor编辑器,下面总结一下遇到的问题以及使用时需要注意的地方: 1. 使用UEditor插件需要先对其进行路径配置: 在ueditor.config.js文 ...

  6. HDU 2037 今年暑假不AC(贪心,区间更新,板子题)

    今年暑假不AC Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Su ...

  7. c语言基础学习03

    =============================================================================涉及到的知识点有:编码风格.c语言的数据类型. ...

  8. 微信小程序开发官方文档解读

    创建页面 在这个教程里,我们有两个页面,index 页面和 logs 页面,即欢迎页和小程序启动日志的展示页,他们都在 pages 目录下.微信小程序中的每一个页面的[路径+页面名]都需要写在 app ...

  9. Centos7安装docker-compse踩过的坑

    一.概要 ​ 本文,我们介绍如何在centos7环境下安装docker-compose, 记录下安装过程步骤以及遇到的问题还有解决办法. 二.安装方式 1.官方安装方式 sudo curl -L ht ...

  10. TypeScript笔记 1--环境搭建

    TypeScript是什么 Typescript是JavaScript的超集,支持ES6特性并且提供了类型系统,可以编译成Javascript.是微软开发且已经在github上开源. ES6(ES20 ...