Git 简明教程

Git 简介

Git 属于分散型版本管理系统,是为版本管理而设计的软件。
Linux 的创始人 Linus Torvalds 在 2005 年开发了 Git 的原型程序。当时,由于在 Linux 内核开发中使用的既有版本管理系统的开发方许可证发生了变更,为了更换新的版本管理系统,Torvalds 开发了 Git。

什么是版本管理

版本管理就是管理更新的历史记录。它为我们提供了一些在软件开发过程中必不可少的功能,例如记录一款软件添加或更改源代码的过程,回滚到特定阶段,恢复误删除的文件等。在 Git 出现以前,人们普遍采用 Subversion 等集中型版本管理系统,而现在 Git 已成为主流。

集中型与分散型

以 Subversion 为代表的集中型,会将仓库集中存放在服务器之中,所以只存在一个仓库。这就是为什么这种版本管理系统会被称作集中型。
集中型将所有数据集中存放在服务器当中,有便于管理的有点。但是一旦开发者所处的环境不能连接服务器,就无法获取最新的源代码,开发也就几乎无法进行。服务器宕机时也是同样的道理,而且万一服务器故障导致数据消失,恐怕开发者就再也见不到最新的源代码了。

以 Git 为代表的分散型,例如 GitHub 会将仓库 Fork 给每一个用户。Fork 就是将 GitHub 的某个特定仓库复制到自己的账户下。Fork 出的仓库与原仓库是两个不同的仓库,开发者可以随意编辑。分散型拥有多个仓库,相对而言稍显复杂。不过,由于本地的开发环境中就有仓库,所以开发者不必连接远程仓库就可以进行开发。

Git 常用命令流程图

Git 客户端

Git 官方客户端
TortoiseGit
SourceTree

初始设置

设置姓名和邮箱地址
首先设置使用 Git 时的姓名和邮箱地址。名字请用英文输入。--global 为全局设置参数。

$ git config --global user.name "<用户名>"
$ git config --global user.email "<用户邮箱>"

这个命令,会在 “~/.gitconfig” 中以如下形式输出设置文件。

[user]
name = <用户名>
email = <用户邮箱>

Git 命令

初始化仓库

$ git init                #初始化仓库
$ git status #查看仓库状态
$ git status -s #查看简单版仓库的状态
$ git show #显示某次提交的内容

添加

$ git add              #向暂存区中添加文件
$ git add . #添加当前目录下的所有文件
$ git add <filename> #添加 <filename> 文件
$ git add -i #交互式添加文件到暂存区
$ git add -p #交互式的保存和取消保存变化

提交

$ git commit                 #提交仓库的历史记录
$ git commit -m "<comment>" #以<comment>作为提交操作的介绍信息进行提交
$ git commit --amend #与上次 commit 合并
$ git commit #在编辑器中记述提交信息的格式如下。
#第一行:用一行文字简述提交的更改内容
#第二行:空行
#第三行以后:记述更改的原因和详细内容

git三棵树
(Source:罗杰·杜德勒)

日志

$ git log                  #查看提交日志
$ git log --pretty=short #只显示提交信息的第一行
$ git log -p <filename> #显示文件 <filename> 的改动
$ git log -5 #显示 5 条日志
$ git log <分支名> #查看某分支的日志

查看某一文件的历史改动

上述命令中 git log -p 可实现查看某一文件的历史改动,不过在 terminal 里面看文本修改为免太不爽,可以试试 gitk 命令:

$ gitk <filename>
$ gitk <file_path>

在查看时文件历史时可以在命令中添加 –follow 参数跟踪文件一切变动,使用此命令时,如果查看的特定文件文件曾重命名,也将被跟踪到并输出历史改动

查找包含特定文件的 commit

$ git log <filename>
$ git show <commit_id>
$ git log --follow <文件绝对路径>

比较

HEAD 表示上一次 commit 的版本
HEAD~n 表示前第 n 次 commit 的版本

$ git diff                     #查看更改前后的差别
$ git diff HEAD #查看工作树和最新 commit 的差别
$ git diff HEAD~1 HEAD #比较上一次和这一次代码之间的差异
$ git diff HEAD~3 HEAD #比较前第三次和这一次代码之间的差异
$ git diff HEAD^^^^^ HEAD #比较前第五次和这一次代码之间的差异
$ git checkout -- <filename> #回滚到修改前的状态

养成这样一个好习惯:在执行 git commit 命令之前先执行 git diff HEAD 命令,查看本次提交与上次提交之间有什么差别,等确认完毕后再进行提交。这里 HEAD 是指向当前分支中最新一次提交的指针。

回退

$ git reset HEAD filename  #从暂存区移除文件
$ git reset --hard HEAD~n #直接回退到前第 n 个版本。
$ git reset --hard SHA #回到 SHA 对应的 commit 的版本。

可选参数:
–hard 回退版本,代码也回退,忽略所有修改
–soft 回退版本,代码不变,回退所有的 add 操作
–mixed 回退版本,代码不变,保留 add 操作

分支

$ git branch <分支名>                           #以 <branch-name> 为名创建新分支
$ git branch -d <分支名> #删除分支
$ git branch –merged & git branch –no-merged #返回已合并或未合并的分支列表
$ git checkout <分支名> #切换分支
$ git merge #与本地当前分支合并。
$ git merge <分支名> #合并分支到当前分支

合并分支
(Source:罗杰·杜德勒)

远程仓库操作

$ git clone <远程仓库网址> <本地目录名>                #将远程仓库克隆到本地
$ git clone -b <分支名> <远程仓库网址> <工作目录名> #克隆远程仓库的特定分支=
$ git clone -o <自定义仓库名> <远程仓库网址> #克隆远程仓库并自定义其名称
$ git fetch <远程仓库名> #从远程库抓取数据
$ git fetch <远程仓库名> <分支名> #从远程库抓取特定分支的数据
$ git remote #查看当前远程库
$ git remote -v #查看远程仓库详细信息
$ git remote <远程仓库名> #查看远程仓库
$ git remote show <远程仓库名> #查看远程仓库信息
$ git remote add <远程仓库名> <远程仓库网址> #添加远程仓库
$ git remote rename <远程仓库名> <新远程仓库名> #重命名远程仓库
$ git remote update <远程仓库名> #更新分支列表
$ git remote rm <远程仓库名> #删除远程仓库
$ git remote add <别名> <远程仓库网址> #设置远程仓库别名
$ git remote prune origin #删除任何不存在于远端仓库的分支
$ git pull #拉取远程库默认分支并合并到当前仓库,相当于 fetch+merge
$ git pull -b <分支名> #拉取远程库特定分支并合并到当前仓库
$ git push origin master #推送数据到默认的远程仓库的主分支
$ git push origin <分支名> #推送数据到默认的远程仓库的特定分支
$ git push origin --tags #推送数据到默认的远程仓库的特定分支并添加标签
$ git push <远程仓库名> <分支名> #推送数据到远程仓库的特定分支
$ git push -u <远程仓库名> <分支名> #指定远程仓库名和分支名为默认值
$ git push -f <远程仓库名> <分支名> #推送数据并强制覆盖到远程仓库的特定分支
$ git push <远程仓库名> <本地分支名>:<远程分支名> #推送数据到远程仓库的特定分支,后者不存在时则新建。

其他

$ git tag                               #列出所有标签
$ git tag -a <标签名> -m "<提交信息>" #为版本打一个标签
$ git reflog #显示本地已完成的操作列表
$ git shortlog -sn #显示提交记录的参与者列表
$ git help #帮助文档
$ git reset #撤销上一次 git add . 操作
$ gitk #打开图形化 git 界面
$ git config --global color.ui auto #彩色的 git 输出
$ git config format.pretty oneline #显示历史记录时,每个提交的信息只显示一行

Git 知识结构
(Source:wustrive2008)

gitignore

对于 Git 项目中不想进行跟踪、同步的文件 / 目录,可将其写入 gitignore 文件中。在 Github 上有人整理了各种开发配置下的 gitignore 文件模板,你可以 在这里 进行挑选。

Q&A

添加 SSH 密钥对

操作远程库(clone/push/pull)时产生如下提示 :

Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights and the repository exists.

这是由于没有将 SSH 密钥对添加到本地 ssh-agent 和 Github 中导致的。如想了解 ssh-agent 原理,可阅读 ssh-agent 与 ssh 的区别,可进行如下操作:

1.确保你已经生成了 SSH 密钥对
2.使用 Git Bash,打开 ssh-agent:

$ eval "$(ssh-agent -s)"
Agent pid 59566

3.添加 SSH 密钥对,默认密钥文件名为 id_rsaid_rsa.pub,可根据需要修改密钥文件名:

$ ssh-add ~/.ssh/id_rsa
Identity added: /c/Users/<用户名>/.ssh/id_rsa (/c/Users/<用户名>/.ssh/id_rsa)

4.登录 Github 用户设置界面,进入 SSH 和 GPG 密钥对界面,将本地的 SSH 公钥(*.pub文件)中的内容复制添加到 Github 的 New SSH key 窗口的 key 中,即可添加成功。

ps. 可在根目录中的 .bash_profile 文件(即 ~/.bash_profile)添加命令别名,今后即可使用别名 sshadd 完成上述操作:

$ alias sshadd='eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_rsa'

或者在该文件中添加如下命令,今后打开 git bash 时即可自动完成上述关于 SSH 密钥的操作:

$ eval "$(ssh-agent -s)" && ssh-add ~/.ssh/github_rsa

Git 的代理设置

在对 Github 某仓库进行 git clone 操作时发现,由于身处强国,下载速度极其不稳定,时快时慢甚至断流导致 clone 失败。所以想到为 Git 设置代理解决问题。
这也算是一个大众问题了,从 这里 看到了答案:

  1. 确认自己使用的代理工具在本地监听的端口 <localport>
  2. 在 git bash 中运行如下命令:

    $ git config --global https.proxy http://127.0.0.1:<localport>
    $ git config --global https.proxy https://127.0.0.1:<localport>

    这样就可以使用 http 或 https 的 URL 进行 clone 操作。
    如果需要取消代理设置:

    $ git config --global --unset http.proxy
    $ git config --global --unset https.proxy

在 Git 官网的 git-config 页面 看到了这样一段话:

可以用 git config 配置 Git。

Git 使用一系列配置文件来保存你自定义的行为。 它首先会查找 /etc/gitconfig文件,该文件含有系统里每位用户及他们所拥有的仓库的配置值。 如果你传递 --system 选项给 git config,它就会读写该文件。

接下来 Git 会查找每个用户的 ~/.gitconfig 文件(或者 ~/.config/git/config 文件)。 你可以传递 --global 选项让 Git 读写该文件。

最后 Git 会查找你正在操作的版本库所对应的 Git 目录下的配置文件(.git/config)。 这个文件中的值只对该版本库有效。

以上三个层次中每层的配置(系统、全局、本地)都会覆盖掉上一层次的配置,所以 .git/config 中的值会覆盖掉 /etc/gitconfig 中所对应的值。

[NOTE]
Git 的配置文件是纯文本的,所以你可以直接手动编辑这些配置文件,输入合乎语法的值。 但是运行 git config 命令会更简单些。

所以我们可以用 git config --help 命令打开帮助页面,研究一下 git config 中各种命令的用途,然后按格式添加在根目录中的 .gitconfig (即 ~/.gitconfig)文件中。比如对照上述设置代理的配置可按如下格式添加:

[http]
proxy = http://127.0.0.1:1080

[https]
proxy = http://127.0.0.1:1080

关于 .gitconfig 应该还有很多玩儿法,如果你有什么好思路不妨在评论区分享给大家。

Git 多账户

Git 使用邮箱进行身份验证,所以 Git 多账户存在以下使用情景:

  • 同一台电脑可有多个使用相同邮箱的 Git 账号,密钥默认读取 id_rsa。 为实现在不同网站以不同用户名,相同邮箱进行操作,可编辑 ~/.ssh/config
host github
hostname github.com
Port 22
host gitlab
hostname gitlab.zjut.com
Port 65095
  • 同一台电脑可有多个使用不同邮箱的 Git 账号,web1 使用 id_rsa 密钥,web2 使用 id_rsa_github 密钥。为实现在不同网站以不同用户名,不同邮箱进行操作,可进行如下操作:

1.编辑 ~/.ssh/config

# web1
Host web1
HostName gitlab.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa
User name1

# web2
Host web2
HostName github.com
PreferredAuthentications publickey
IdentityFile ~/.ssh/id_rsa_github
User name2

2.取消用户名和邮箱地址的全局设置

$ git config --global --unset user.name
$ git config --global --unset user.email

3.进入每个项目单独设置自己的用户名和邮箱地址

$ git config  user.name "<用户名>"
$ git config user.email "<用户邮箱>"

4.测试是否配置成功

$ ssh-T git@gitlab.com              #使用 HostName 测试
$ ssh-T git@web2 #使用 Host 测试

更新 Github 中 fork 的项目

在 Github 上 fork 了一些牛人的项目,但自己 fork 的项目是静止不变的,不会随原项目的更新而更新。当原项目更新时,我们可使用如下方法更新自己仓库中相应的项目:
1.克隆自己的项目到本地:

$ git clone <自己的远程仓库网址>

2.进入克隆到本地的项目目录,把 fork 的原项目作为一个远程仓库以 upstream 为别名添加到本地库中:

$ git remote add upstream <原远程仓库网址>

3.拉取另一个远程仓库的相应分支合并到本地库:

$ git pull upstream master

4.将合并后的项目推送到 Github 上自己的项目中:

$ git push origin master

追加改动的 commit

当完成某一次 commit 后又进行了代码改动,但不想再提交一个新的 commit,这时可以使用如下命令追加改动文件到前一个 commit:

$ git add <文件名>                #将修改的文件添加到暂存区
$ git commit --amend -C HEAD #追加改动到上一次 commit

暂时处理其他项目的修改

当你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态,而你想转到其他分支上进行一些工作。问题是,你不想提交进行了一半的工作,否则以后你无法回到这个工作点。这时候可以将现场储藏起来,然后处理插队需求或切换到其他分支工作,之后再将现场取出继续工作:

$ git stash           #储藏现场以便之后继续工作
$ git stash list #查看所有被储藏的现场列表
$ git stash apply #恢复现场,但是不删除现场备份
$ git stash pop #恢复现场,同时删除现场备份
$ git stash drop #删除现场备份

可以从 pop 关键字看出储藏结构是堆栈,因此你可以在这个上面玩儿出其他花样。

使用外部软件进行 diff/merge

作为一个伪程序狗,终究还是不能逃脱使用 GUI 的宿命。由于 Git 自带的 git diff 并不能够满足某些场景下的使用。我们需要设置一些第三方对比软件进行新旧文档的比较 / 合并。这里笔者使用 Beyond Compare 4 进行举例:

git diff 是普通的逐行对比命令,使用外部比较工具需要使用 git difftool 命令,假设 Beyond Compare 4 已安装至 E:\Program Files\Beyond Compare 4 目录,在使用前我们需进行如下配置,方法有二:

  1. 执行 Git 命令进行配置:

    $ git config --global diff.tool bc4
    $ git config --global difftool.bc4.path "e:/program files/beyond compare 4/bcomp.exe"
    $ git config --global merge.tool bc4
    $ git config --global mergetool.bc4.path "e:/program files/beyond compare 4/bcomp.exe"
  2. 修改 . gitconfig 文件进行配置,添加如下项:

    [diff]
    tool = bc
    [difftool]
    prompt = false
    [difftool "bc"]
    path = e:/program files/beyond compare 4/bcomp.exe
    [merge]
    tool = bc
    [mergetool "bc"]
    path = e:/program files/beyond compare 4/bcomp.exe

Tips

列举一些实用的 Alias,可根据自己需要酌情修改,并将其写入 .bash_profile 文件中实现自动加载:

alias g=git
alias ga='git add'
alias gaa='git add --all'
alias gap='git add --patch'
alias gb='git branch'
alias gba='git branch -a'
alias gbr='git branch --remote'
alias gc='git commit -v'
alias 'gc!'='git commit -v --amend'
alias gca='git commit -v -a'
alias 'gca!'='git commit -v -a --amend'
alias gcl='git config --list'
alias gclean='git reset --hard && git clean -dfx'
alias gcm='git checkout master'
alias gcmsg='git commit -m'
alias gco='git checkout'
alias gcount='git shortlog -sn'
alias gcp='git cherry-pick'
alias gcs='git commit -S'
alias gd='git diff'
alias gdc='git diff --cached'
alias gdt='git difftool'
alias gg='git gui citool'
alias gga='git gui citool --amend'
alias ggpnp='git pull origin `git rev-parse --abbrev-ref HEAD` && git push origin `git rev-parse --abbrev-ref HEAD`'
alias ggpull='git pull origin `git rev-parse --abbrev-ref HEAD`'
alias ggpur='git pull --rebase origin `git rev-parse --abbrev-ref HEAD`'
alias ggpush='git push origin `git rev-parse --abbrev-ref HEAD`'
alias gignore='git update-index --assume-unchanged'
alias gignored='git ls-files -v | grep "^[[:lower:]]"'
alias git-svn-dcommit-push='git svn dcommit && git push github master:svntrunk'
alias gk='gitk --all --branches'
alias gl='git pull'
alias glg='git log --stat --max-count=10'
alias glgg='git log --graph --max-count=10'
alias glgga='git log --graph --decorate --all'
alias glo='git log --oneline --decorate --color'
alias globurl='noglob urlglobber '
alias glog='git log --oneline --decorate --color --graph'
alias glp=_git_log_prettily
alias gm='git merge'
alias gmt='git mergetool --no-prompt'
alias gp='git push'
alias gpoat='git push origin --all && git push origin --tags'
alias gr='git remote'
alias grba='git rebase --abort'
alias grbc='git rebase --continue'
alias grbi='git rebase -i'
alias grep='grep --color=auto --exclude-dir={.bzr,.cvs,.git,.hg,.svn}'
alias grh='git reset HEAD'
alias grhh='git reset HEAD --hard'
alias grmv='git remote rename'
alias grrm='git remote remove'
alias grset='git remote set-url'
alias grt='cd $(git rev-parse --show-toplevel || echo ".")'
alias grup='git remote update'
alias grv='git remote -v'
alias gsd='git svn dcommit'
alias gsps='git show --pretty=short --show-signature'
alias gsr='git svn rebase'
alias gss='git status -s'
alias gst='git status'
alias gsta='git stash'
alias gstd='git stash drop'
alias gstp='git stash pop'
alias gsts='git stash show --text'
alias gts='git tag -s'
alias gunignore='git update-index --no-assume-unchanged'
alias gunwip='git log -n 1 | grep -q -c "\-\-wip\-\-" && git reset HEAD~1'
alias gup='git pull --rebase'
alias gvt='git verify-tag'
alias gwc='git whatchanged -p --abbrev-commit --pretty=medium'
alias gwip='git add -A; git ls-files --deleted -z | xargs -r0 git rm; git commit -m "--wip--"'
alias gad='git diff --name-only --diff-filter=M --relative | xargs -rt git add'
alias log="git log --oneline --graph --decorate --color=always"
alias logg="git log --graph --all --format=format:'%C(bold blue)%h%C(reset) - %C(bold green)(%ar)%C(reset) %C(white)%s%C(reset) %C(bold white)— %an%C(reset)%C(bold yellow)%d%C(reset)' --abbrev-commit --date=relative"

Git Cheat Sheet


相关文章
廖雪峰Git系列教程
Git 学习之基本操作(一)
Git 入门
Git 安装和初始设置
常用的 12 个 Git 基本命令
git 和 svn diff 命令行可视效果
关于 Git 和 Github 你不知道的十件事
Git 常用命令和 Git 团队使用规范指南
Git 的多账号如何处理?
Git 常用命令
git版本管理策略及相关技巧(A)