本文主要介绍一下git工具的使用。为了使后面的学习更顺利,记住下面这些关于Git的概念。Git有三种状态,你的文件可能处于其中之一:

  • 已修改(mofified)

  • 已暂存(staged)

  • 已提交(committed)

已修改(modified)表示修改了文件,但还没保存到数据库中; 已暂存(staged)表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中; 已提交(committed)表示数据已经安全的保存在本地数据库中。

由此,引入Git项目的三个工作区的概念: Git仓库、工作目录、暂存区,如下图所示:

git-workspace

Git仓库目录是Git用来保存项目的元数据和对象数据库的地方。这是Git中最重要的部分,从其他计算机克隆仓库时,拷贝的就是这里的数据。

工作目录是对项目的某个版本独立提取出来的内容。这些从Git仓库的压缩数据库中提出出来的文件,放在磁盘上供你使用或修改。

暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在Git仓库目录中。有时候也被称作“索引”,不过一般说法还是叫暂存区域。

基本的Git工作流程如下:

1) 在工作目录中修改文件

2) 暂存文件,将文件的快照放入暂存区域

3) 提交更新,找到暂存区域的文件,将快照永久性存储到Git仓库目录

如果Git目录中保存着的特定版本文件,就属于已提交状态;如果作了修改并已放入暂存区域,就属于已暂存状态;如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。在后续,你会进一步了解这些状态的细节,并学会如何根据文件状态实施后续操作,以及怎样跳过暂存直接提交。

1. Git的安装设置

1.1 Windows下Git工具的安装

1) 到git官方网站去下载合适版本的git客户端,然后基本按默认方式安装即可。

2) TortoiseGit是给git安装一个外壳,以使git工具的使用更加方便。可以到TortoiseGit官方网站下载相应的安装包安装即可。

1.2 Linux下Git工具的安装

在Centos7环境下, 我们可以执行如下命令直接安装Git:

# yum install git
# git --version
git version 1.8.3.1

# git --help
usage: git [--version] [--help] [-c name=value]
           [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]
           [-p|--paginate|--no-pager] [--no-replace-objects] [--bare]
           [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]
           <command> [<args>]

The most commonly used git commands are:
   add        Add file contents to the index
   bisect     Find by binary search the change that introduced a bug
   branch     List, create, or delete branches
   checkout   Checkout a branch or paths to the working tree
   clone      Clone a repository into a new directory
   commit     Record changes to the repository
   diff       Show changes between commits, commit and working tree, etc
   fetch      Download objects and refs from another repository
   grep       Print lines matching a pattern
   init       Create an empty Git repository or reinitialize an existing one
   log        Show commit logs
   merge      Join two or more development histories together
   mv         Move or rename a file, a directory, or a symlink
   pull       Fetch from and merge with another repository or a local branch
   push       Update remote refs along with associated objects
   rebase     Forward-port local commits to the updated upstream head
   reset      Reset current HEAD to the specified state
   rm         Remove files from the working tree and from the index
   show       Show various types of objects
   status     Show the working tree status
   tag        Create, list, delete or verify a tag object signed with GPG

1.3 使用前配置

Git自带一个git config的工具来帮助设置控制Git外观和行为的配置变量。这些变量存储在三个不同的位置:

1. /etc/gitconfig文件: 包含系统上每一个用户及他们仓库的通用配置。如果使用带有 --system 选项的 git config 时,
   它会从此文件读写配置变量

2. ~/.gitconfig 或 ~/.config/git/config文件: 只针对当前用户。可以传递 --global 选项让Git读写此文件。 

3. 当前使用仓库的Git目录中的config文件(就是.git/config): 针对该仓库

每一个级别覆盖上一级别的配置,所以.git/config的配置变量会覆盖/etc/gitconfig中的配置变量。

在Windows系统中, Git会查找$HOME目录下(一般情况下是C:\Users\$USER)的.gitconfig文件。Git同样也会寻找/etc/gitconfig文件,但只限于MSys的根目录下,即安装Git时所选的目标位置。

1) 配置用户信息

当安装完Git应该做的第一件事就是设置用户名称邮件地址。这样做很重要,因为每一个Git的提交都会使用这些信息,并且它会写入到每一次提交中,不可更改:

# git config --global user.name "ivan1001"
# git config --global user.email "ivan1001@github.com"
# git config --list
user.name=ivan1001
user.email=ivan1001@github.com

再次强调,如果使用了--global选线,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情,Git都会使用这些信息。当你想针对特定项目使用不同的用户名称和邮件地址时,可以在那个项目目录下运行不使用--global选项的命令来配置。

注意此处用户名和邮箱是git提交代码时用来显示你身份和联系方式的,并不是github用户名和邮箱。另外,很多GUI工具都会在第一次运行时帮助你配置这些信息。

如果要删除,可以使用--unset选项:

# git config --global --unset user.name

有时候我们在客户端使用git时,为了方便会记住用户名、密码,但是假如密码进行了修改,这时我们可以通过如下命令删除相应的凭据,这样我们就可以重新输入用户名、密码:

# git config --system --unset credential.helper

2) 文本编辑器

既然用户信息已经设置完毕,你可以配置默认文本编辑器了。当Git需要您输入信息时会调用它。如果未配置,Git会使用操作系统默认的文本编辑器,通常是vim。如果你想使用不同的文本编辑器,例如Emacs,可以这样做:

# git config --global core.editor emacs

3) 检查配置信息

如果想要检查你的配置,可以使用git config --list命令来列出所有Git当时能找到的配置:

# git config --list
user.name=ivan1001
user.email=ivan1001@github.com
core.repositoryformatversion=0
core.filemode=true
core.bare=false
core.logallrefupdates=true

上面的输出结果中,可能会看到重复的变量名,因为Git会从不同的文件中读取同一个配置(例如: /etc/gitconfig~/.gitconfig)。这种情况下,Git会使用它找到的每一个变量的最后一个配置。

可以通过输入git config <key>来检查Git的某一项配置:

# git config user.name
ivan1001

4) 忘记密码

有时候我们git可能记住了我们的用户名、密码,当我们修改了新密码之后,再自动采用原来的旧密码肯定是不行的,此时我们可以通过执行如下命令:

# git config --system --unset credential.helper

5) 获取帮助

若在使用Git时需要获取帮助,有三种方法可以找到Git命令的使用手册:

# git help <verb>

# git <verb> --help

# man git-<verb>

例如,要想获得config命令手册,执行:

# git help config

2. Git使用ssh密钥

Git支持httpsgit两种传输协议,github分享时会有两种协议可选,如下图所示:

git-https-ssh

两种协议的地址类似于如下:

https协议     https://github.com/ivanzz1001/leetcode.git

git协议       git@github.com:ivanzz1001/leetcode.git

git工具使用https协议时,每次pull、push都会提示要输入密码; 当使用git协议,然后使用ssh密钥,这样免去每次都输密码的麻烦。

初次使用git的用户要使用git协议大概需要三个步骤:

  • 生成密钥对

  • 设置远程仓库(本文以github为例)上的公钥

  • 把git的remote url修改为git协议

对于前2个步骤,在初次设置以后,后面使用都不需要再次设置; 而步骤3视以后项目的remote url而定,如果以后其他项目的协议为https,则需要此步骤。

2.1 生成密钥对

大多数Git服务器都会选择使用SSH公钥来进行授权。系统中的每个用户都必须提供一个公钥用于授权,没有的话就要生成一个。生成公钥的过程在所有操作系统上都差不多。

首先你要确认一下本机是否已经有一个公钥。SSH公钥默认存储在账户主目录下的~/.ssh文件夹中,进去看看:

# cd ~/.ssh
# ls
# authorized_keys2  id_dsa       known_hosts config            id_dsa.pub

看一下有没有id_rsa和id_rsa.pub(或者是id_dsa和id_dsa.pub)之类成对的文件。有.pub后缀的文件就是公钥,另一个文件则是密钥。

假如没有这些文件,甚至连.ssh目录都没有,可以使用ssh-keygen来创建。该程序在Linux/Mac系统上由SSH包提供,而在Windows上则包含在MSysGit包里:

# ssh-keygen -t rsa -C "your_email@youremail.com"

Creates a new ssh key using the provided email # Generating public/private rsa key pair.

Enter file in which to save the key (/home/you/.ssh/id_rsa):

直接按Enter键就行。然后,会提示你输入密码,如下(建议输入一个,安全一点,当然不输也行,应该不会有人闲的无聊冒充你去修改你的代码):

Enter same passphrase again: [Type passphrase again]

完成了之后,大概是这样:

Your public key has been saved in /home/you/.ssh/id_rsa.pub.
The key fingerprint is: # 01:0f:f4:3b:ca:85:d6:17:a1:7d:f0:68:9d:f0:a2:db your_email@youremail.com

到此为止,你本地的密钥对就生成了。

2.2 添加公钥到你的远程仓库

1) 查看你生成的公钥

cat ~/.ssh/id_rsa.pub

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC0X6L1zLL4VHuvGb8aJH3ippTozmReSUzgntvk434aJ/v7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8vR3c8E7CjZN733f5AL8uEYJA+YZevY5UCvEg+umT7PHghKYaJwaCxV7sjYP7Z6V79OMCEAGDNXC26IBMdMgOluQjp6o6j2KAdtRBdCDS/QIU5THQDxJ9lBXjk1fiq9tITo/aXBvjZeD+gH/Apkh/0GbO8VQLiYYmNfqqAHHeXdltORn8N7C9lOa/UW3KM7QdXo6J0GFlBVQeTE/IGqhMS5PMln3 admin@admin-PC

2) 登录你的Github账户。点击你的头像,然后Settings -> 左栏点击SSH and GPG keys -> 点击New SSH key

3) 复制上面的公钥内容,粘贴进Key文本域内。title域,自己随便起个名字

4) 点击Add key

完成以后,验证下这个key是不是正常工作:

# ssh -T git@github.com

Attempts to ssh to github

如果看到:

Hi xxx! You've successfully authenticated, but GitHub does not # provide shell access.

恭喜你,你的设置已经成功了。

2.3 修改git的remote url

使用命令git remote -v查看你当前的remote url:

# git remote -v
origin https://github.com/someaccount/someproject.git (fetch)
origin https://github.com/someaccount/someproject.git (push)

如果是以上的结果,说明此项目是使用https协议进行访问的; 如果地址是以git开头,则表示使用git协议。

你可以登录你的github,在上面你可以看到你的ssh协议相应的url,类似:

git-ssh

复制此ssh链接,然后使用命令git remote set-url来调整你的url:

# git remote set-url origin git@github.com:someaccount/someproject.git

然后,你可以再用命令git remote -v查看一下,url是否已经变成了ssh地址。

然后你就可以愉快的使用git fetch、git pull、git push,再也不用输入烦人的密码了。

3. Git的简单使用

1) 创建一个新的repository

# git clone http://10.16.28.73/harbor/harbor.git
# cd harbor
# touch README.md
# git add README.md
# git commit -m "add README"
# git push -u origin master

2) 从一个已存在的文件夹与某个repository关联

# cd existing_folder
# git init
# git remote add origin http://10.16.28.73/harbor/harbor.git
# git add .
# git commit -m "Initial commit"
# git push -u origin master

注: 上述http://10.16.28.73/harbor/harbor.git必须事先存在

3) 将一个已存在repository与另一个repository关联

# cd existing_repo
# git remote add origin http://10.16.28.73/harbor/harbor.git
# git push -u origin --all
# git push -u origin --tags

4)删除一个文件,并提交

# git rm test.txt
# git commit -m "remove test.txt"
# git push -u origin master

上面删除一个文件test.txt,并提交。

5) 修改一个文件并提交

# git add test.txt
# git commit -m "modified test.txt"
# git push -u origin master

上面假设修改了test.txt文件,然后提交到github上。这里向github提交修改后的文件的命令与新添加文件一直。

4. Git clone克隆大项目

当我们在使用git clone命令来克隆一些大的工程时,可能经常会出现卡死的情况,此时我们可以尝试调整如下两个参数:

# git config --global core.compression 0
# git config --global http.postBuffer 1073741824
# git clone --depth 1 <项目地址>

在git clone时加上--depth时, depth用于指定克隆深度,为1即表示只克隆最近一次commit。这种方法克隆的项目只包含最近的一次commit的一个分支,体积很小,即可解决项目过大导致的timeout的问题,但会产生另外一个问题,他只会把默认分支克隆下来,其他远程分支并不在本地,所以这种情况下,需要用如下方法拉取其他分支:

# git clone --depth 1 https://github.com/dogescript/xxxxxxx.git
# git remote set-branches origin 'remote_branch_name'
# git fetch --depth 1 origin remote_branch_name
# git checkout remote_branch_name

此外,还可以尝试采用如下方法:

1) 运行以下命令进行clone

# git clone --recursive <URL>

此处--recursive主要是用于克隆含有子模块的工程,用于循环克隆子项目

2) 进入项目根目录,继续下载

# cd <project_dir>
# git submodule update --init --recursive

5. git克隆特定commit

当我们阅读一个大点的开源项目,一个麻烦点是: 远程仓库的代码量太庞大了,如果我需要通过

# git reset --hard [commit sha1]

来切换到感兴趣的commit快照,就首先得git clone整个远程仓库,时间需要等待太长,而且网络一旦有问题,还会功亏一篑。所以有没有一种只拉取远程仓库中的某个commit呢?有,命令行如下:

# make a new blank repository in the current directory
git init

# add a remote
git remote add [nick_name] [url://to/source/repository]

# fetch a commit (or branch or tag) of interest
# Note: the full history of this commit will be retrieved
git fetch [nick_name] <sha1-of-commit-of-interest>

# reset this repository's master branch to the commit of interest
git reset --hard FETCH_HEAD

5.1 改进方法

git clone默认是取回master分支,可以使用-b参数指定分支。

实际上,-b参数不仅支持分支名,还支持tag名等。具体语法如下:

# git clone <remote-addr:repo.git> -b <branch-or-tag-or-commit>

其实,clone回来是包含了该branch完整历史的,所以仍然会有比较多的文件传输。

下面提供另一个步骤:

1) 选择一个包含目标commitID的branch或tag,并指定depth=1以获得比较少的额外文件传输:

# git clone --depth 1 <remote-addr:repo.git> -b <branch-or-tag>

2) clone完成后,进入目录,执行

# git fetch --depth <a-number>

3) 不断增大步骤2)的数字,直到找到你要的commit

4) 最后采用如下命令切换到对应的commit

# git checkout <commitID>

注: 可以通过如下方式来查询提交历史

# git log -s
commit 5dc1e4c05cb68dbf62ae6fce3f0700e4654fdbbe
Author: Jenkins Build Slave User <ceph-release-team@redhat.com>
Date:   Wed Oct 4 14:17:25 2017 +0000

    10.2.10



[参看]

  1. git教程

  2. git创建远程仓库并上传代码到远程仓库中

  3. git book

  4. Git 基础再学习之:git checkout – file

  5. git使用ssh密钥

  6. git 不用clone整个远程仓库,只把特定的commit给fetch下来的方案

  7. Git切换分支到指定的提交(commit)