Git 教程
Git
Git 安装配置
配置环境变量
右击此电脑 → 属性 → 高级系统设置 → 环境变量 → 在系统变量中找到 path,添加 安装目录 \ cmd\git.exe
进入到 GIT 安装目录下双击 git-bash.exe,打开 git 命令行窗口,依次输入以下命令
# 配置提交代码时的签名和绑定全局邮箱 git config --global user.name "shukaiYoung" git config --global user.email "1184590995@qq.com"
生成 ssh-key
输入以下命令生成 ssh-key,输入后连续按 3 次回车
ssh-keygen -t rsa -C "1184590995@qq.com"
进入目录
C:\Users\YangShuKai\.ssh,可以看到生成的 id_rsa 以及 id_rsa.pub,前者是私钥最好不要泄露,后者是公钥可以告诉别人。将客户端与
Github进行连接进入到
Github首页,依次点击进入用户头像 → setting → SSH and GPG keys → New SSH key
title 可以自定义,用文本编辑器打开 id_rsa.pub 将里面的内容复制到 key ,Add SSH key。
连接完毕
Git 工作流程图

git fetch 是将远程主机的最新内容拉到本地,用户在检查了以后决定是否合并到工作本机分支中。
git pull 则是将远程主机的最新内容拉下来后直接合并,即:git pull = git fetch + git merge,这样可能会产生冲突,需要手动解决。
git merge 和 git rebase 的区别
git rebase 和 git merge 一样都是用于从一个分支获取并且合并到当前分支,但是他们采取不同的工作方式,采用 git merge 和 git rebase 后,git log 的区别,merge 命令不会保留 merge 的分支的 commit

如图所示:假如你在一个 feature 分支进行新特性的开发,与此同时,master 分支的也有新的提交。

为了将 master 上新的提交合并到你的 feature 分支上,你有两种选择:git merge or git rebase。
merge
执行命令:
git checkout feature (切换当前分支到 feature 分支)
git merge master(将 master 分支合并到当前分支)
或者执行更简单的:
git merge master feature(将 master 分支合并到 feature 分支)
那么此时在 feature 上 git 自动会产生一个新的 commit(merge commit)

git merge :
特点:自动创建一个新的 commit,如果合并的时候遇到冲突,仅需要修改后重新 commit
优点:记录了真实的 commit 情况,包括每个分支的详情
缺点:因为每次 merge 会自动产生一个 merge commit,所以在使用一些 git 的 GUI tools,特别是 commit 比较频繁时,看到分支很杂乱。
使用 git merge 命令合并分支只有在解决完冲突时才会自动产生一个 commit,如果想在没有冲突的情况下也自动生成一个 commit,记录此次合并就可以用:git merge --no-ff 命令,如果不加 --no-ff 则被合并的分支之前的 commit 都会被抹去,只会保留一个解决冲突后的 merge commit。

rebase
本质时变基 变基 变基
执行以下命令:
git checkout feature
git rebase master

rebase 特点:会合并之前的 commit 历史
优点:得到更简洁的项目历史,去掉了 merge commit
缺点:如果合并出现代码问题不容易定位,因为 re-write 了 history

合并时如果出现冲突需要按照如下步骤解决
- 修改冲突部分
- git add
git rebase --continue- (如果第三步无效可以执行
git rebase --skip)
也可以:
- (一股脑)使用
merge命令合并分支,解决完冲突,执行git add .和git commit -m'fix conflict'。这个时候会产生一个 commit。 - (交互式)使用
rebase命令合并分支,解决完冲突,执行git add .和git rebase --continue,不会产生额外的 commit。这样的好处是,‘干净’,分支上不会有无意义的解决分支的 commit;坏处,如果合并的分支中存在多个commit,需要重复处理多次冲突。
不要在 git add 之后习惯性的执行 git commit 命令
Rebasing rebase 的黄金法则
never use it on public branches(不要在公共分支上使用)
比如说如下场景:如图所示

如果你 rebase master 到你的 feature 分支:
rebase 将所有 master 的 commit 移动到你的 feature 的顶端。问题是:其他人还在 original master 上开发,由于你使用了 rebase 移动了 master,git 会认为你的主分支的历史与其他人的有分歧,会产生冲突。
所以在执行 git rebase 之前 问问自己,
会有其他人看这个分支么?
if YES 不要采用这种带有破坏性的修改 commit 历史的 rebase 命令
if NO ok,随你便,可以使用 rebase
git merge 和 git rebase 该如何选择?
如果你想要一个干净的,没有 merge commit 的线性历史树,那么你应该选择
git rebase如果你想保留完整的历史记录,并且想要避免重写 commit history 的风险,你应该选择使用
git merge
也可以两者结合:
- 获取远程项目中最新代码时:
git pull --rebase,这个时隐性的合并远程分支的代码不会产生而外的 commit(但是如果存在冲突的 commit 太多就像上面说的,需要处理很多遍冲突)。 - 合并到分支的时候:
git merge --no-ff,自动一个 merge commit,便于管理(这看管理人员怎么认为了)
补充:
merge 命令下会生成代码分支历史。rebase 永远不会导致多个历史分支进行交织。它永远都是一条线。纯洁而又干脆。轻轻爽爽的, 从不拖泥带水。Merge 命令会保留所有 commit 的历史时间。每个人对代码的提交是各式各样的。尽管这些时间对于程序本身并没有任何意义。但是 merge 的命令初衷就是为了保留这些时间不被修改。这样也就形成了以 merge 时间为基准的网状历史结构。每个分支上都会继续保留各自的代码记录, 主分支上只保留 merge 的历史记录。子分支随时都有可能被删除。子分子删除以后,你能够看到的记录也就是,merge 某 branch 到某 branch 上了。这个历史记录描述基本上是没有意义的。还有一个是你不能,也不应该去修改这个历史记录描述。那是因为这个 merge 记录里面,不仅仅包含你自己的代码,也包含别人的代码。到这里你能想象有多乱吧?rebase 命令会始终把最新的修改放到最前头。比如对主 branch 进行 rebase 以后, 所有修改就会在主 branch 当前所有的修改之前。你会更有信心保证你的代码运行畅通无阻。通过你自己的测试以后, 你就可以放心的把代码合并到主的 branch 里面了。rebase 通常是发生在自己的个人 branch 上的。它的基础就是现有的主 branch。这样做的好处就是保证每个人的代码都可以运行在当前最新的主 branch 的代码上。
提交本地项目到 GitHub 的流程
- 先在 GitHub 上新建一个仓库


创建成功后的仓库

- 对本地项目进行初始化
在本地项目目录下右键,选择 Git Bash Here
在命令行窗口输入 git init,初始化本地仓库

本地仓库关联远程仓库(下划线为远程仓库地址)
git remote add origin git@github.com:XXXX/nothing2.git

输入命令:

从远程主机的 master 分支拉取最新内容
git fetch origin master(master 为远程仓库的分支名)
使用 fetch 获取时,并未合并到本地仓库,此时可使用 git merge 实现远程仓库副本与本地仓库的合并。
若执行此命令,可在执行命令:git merge FETCH_HEAD,此命令将拉取下来的最新内容合并到当前所在的分支,执行这两步命令后不用在第 6 个步骤。相当于 git fetch + git merge = git pull
下面红色勾选的为可使用的分支名

新建的仓库默认只有主分支(master),但在实际工作中一般会有多个分支,开发分支(develop),测试分支(test)等等,主分支用来推送稳定版本,因为仓库是新建的只有主分支(master),下面的演示以主分支为例。
拉取远程仓库的 master 分支,输入命令:git fetch origin master

在远程仓库创建名称为 XXX 的新分支并切换到该分支
git checkout -b XXX(远程分支名称) origin/XXX(远程分支名称)
命令:git checkout -b master origin/master
因为 master 分支创建仓库时默认创建,且默认在 master 分支下,所以控制台会提示”Already on ‘master’ “即当前已在 master 分支,如果是单分支开发就不用进行此步骤了,如果是多分支开发,在选择执行此步骤。

将远程主机的某个分支的内容更新取回,并与本地指定的分支合并,这里为拉取远程 master 分支(** 新项目提交之前最好先执行此步操作或前面第 4 个步骤,否则会因为远程仓库的文件和本地仓库文件不一致而发生错误,因为此时远程仓库中可能含有. gitignore、README.md、LICENSE(新建仓库时产生)等文件,而本地仓库没有,提交时因为两处文件不一致而导致提交失败 **)
完整格式:git pull <远程主机名> <远程分支名>:<本地分支名>
- 如果远程分支是与当前分支合并,则冒号后面的部分可以省略。*
命令:git pull origin master
有时需要输入你的账号密码:



到本地项目中查看,已完成拉取远程某个分支到本地

可以编辑本地项目中的. gitignore 文件,右键用记事本打开,修改其中的配置,此配置文件是在配置提交文件到远程仓库时,要忽略该文件夹下的哪些文件,下面的配置可供参考,按需设置:
#忽略编译完成的. class 文件 *.class #忽略日志文件. log *.log #忽略. ctxt 结尾的文件 *.ctxt # 忽略 Java 移动工具包 Mobile Tools for Java (J2ME) .mtj.tmp/ #忽略. jar、.war、.nar、.ear、.zip、.tar.gz、.rar 结尾的文件 *.jar *.war *.nar *.ear *.zip *.tar.gz *.rar #忽略虚拟机奔溃日志 virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* #忽略所有. svn 目录 .svn/ #忽略所有 target 目录 target/ #忽略所有. idea 目录 .idea/ #忽略所有. iml 文件 *.iml #忽略所有 out 目录 out/ #忽略所有. mvn 目录 .mvn/下面进入提交本地项目到远程仓库的步骤:
提交文件到暂存区
在本地项目目录下右键,选择 Git Bash Here
在命令行窗口输入 git add . ,提交所有文件到暂存区,“." 代表该目录下所有文件
命令:git add .

将暂存区中的文件提交到本地版本库
git commit -m “first commit”(git commit -m 后面的内容相当于给本次提交加一个注释,不要忘记了双引号)
命令:git commit -m "first commit"

到 commit 这一步,只是完成了本地提交。为什么有一个 add 一个 commit 的过程呢?在 git 中有一个暂存区的概念,add 就是将代码从工作区提交到暂存区;commit 的过程就是将代码从暂存区提交到版本库。git add . 是提交全部文件到暂存区,git commit -m 后面相当于给本次提交加一个注释。
这里解释一下 git 为什么有暂存区的概念,这是和 SVN 所不同的。git 是分为工作区和版本库的,工作区就是我们本地看到的工作目录,版本库就是你工作目录中的. git 文件。

版本库中存放了很多东西,比如 stage, 就是我们上面提到的暂存区,还有自动创建的 master 分支,还有指向 master 的 head 指针……. 我们不能手动修改版本库内的任何数据,否则可能会造成不可预期的错误。所以我们上面,将工作区的代码提交到版本库是分为两个步骤的,第一步是通过 add 命令将文件添加到暂存区 stage 中;第二步将暂存区中的数据提交到版本库中,用 commit 命令,其实就是将 stage 中的内容提交到当前分支。我们在 GitHub 上创建 git 版本库时,会为我们自动创建一个 master 主分支,当然我们还可以手动创建其他分支,当前分支是什么,我们 commit 的时候就将 stage 的内容提交到该分支上。
将版本库中的代码提交到远程分支上去
git push -u origin master(远程分支名)
命令:git push -u origin master

出现以上错误是因为网络不好,再试一次即可。

提交成功啦!
刷新 GitHub 上的仓库,可以看到本地项目已提交到远程仓库中。

撤销 git add
git add . (空格 + 点) 表示当前目录所有文件,不小心就会提交其他文件
git add 如果添加了错误的文件的话
撤销操作如下:
**git status 先看一下 add 中的文件 **
**git reset HEAD 如果命令后面没有添加任何参数 就是上一次 add 里面的全部撤销了 **
**git reset HEAD XXX/XXX/XXX.java 就是对某个文件进行撤销了 **
撤销 git commit
写完代码后,一般这样
git add . // 添加所有文件
git commit -m "本功能全部完成"
执行完 commit 后,想撤回 commit,怎么办?
可以执行以下命令
git reset --soft HEAD^
这样就成功的撤销了 commit
注意,仅仅是撤回 commit 操作,写的代码仍然保留。
HEAD^ 的意思是上一个版本,也可以写成 HEAD~1
如果进行了 2 次 commit,想都撤回,可以使用 HEAD~2
至于这几个参数:
--mixed :不删除工作空间改动代码,撤销 commit,并且撤销 git add . 操作
这个为默认参数,git reset --mixed HEAD^ 和 git reset HEAD^ 效果是一样的。
--soft :不删除工作空间改动代码,撤销 commit,不撤销 git add .
--hard:删除工作空间改动代码,撤销 commit,撤销 git add .
注意完成这个操作后,就恢复到了上一次的 commit 状态。
如果 commit 注释写错了,只是想改一下注释,只需要:git commit --amend
此时会进入默认 vim 编辑器,修改注释完毕后保存就好了。
提交本地项目到 GitHub 被拒绝

原因:GitHub 中的某些文件(如 README.md、.gitignore 等文件 创建远程仓库时生成的文件)不在本地项目中或者本地项目中的其他文件和远程仓库的文件存在冲突。
可以通过执行如下命令进行文件合并:
git pull --rebase origin master
执行上面的代码就可以使 GitHub 远程仓库中和本地项目同步了,再次进行 push 即可。
git 常用命令
| 新建 | ||
|---|---|---|
| 克隆远程仓库到本地 | git clone https://github.com/XXXXXX/XXXX.git | git clone 的内容为远程仓库地址 |
| 新建一个新的本地仓库 | git init | |
| 本地更改 | ||
| 查看本地工作目录中已更改的文件 | git status | 显示工作目录和暂存区的状态。使用此命令能看到哪些修改被暂存到了, 哪些没有, 哪些文件没有被 Git 追踪到(即还没有进行 git add 的文件)。git status 不显示已经 commit 到项目历史中去的信息 |
| 比较两次修改的差异 | git diff | 查看尚未暂存的文件更新了哪些部分,不加参数直接输入,此命令比较的是工作目录 (Working tree) 和暂存区域快照 (index) 之间的差异也就是修改之后还没有暂存起来的变化内容 |
| 将所有当前更改添加到下一次提交 | git add . | |
| 将指定文件的一些更改添加到下一次提交 | git add -p | 代表某文件 |
| 在跟踪的文件中提交所有本地更改 | git commit -a | |
| 更改最后一次提交 | git commit --amend | 不要随便修改已发布的提交! |
| 提交当前更改 | git commit -m “XXXX” | XXX 表示提交的注释信息,不要忘记注释信息上的双引号 |
| 提交历史 | ||
| 显示所有提交,从最初的提交开始显示 | git log | |
| 显示指定文件的提交历史 | git log -p | 代表某文件 |
| 显示是谁在什么时候修改了此文件,以及修改了什么内容 | git blame | 代表某文件 |
| 分支和标签 | ||
| 显示所有存在的分支 | git branch -av | |
| 切换到指定分支 | git checkout | 代表要切换的分支 |
| 新建一个分支 | git branch | 代表新的分支的名称 |
| 在远程分支上创建一个新的分支 | git checkout --track <remote/branch> | <remote/branch > 代表新的远程分支名称 |
| 删除一个本地分支 | git branch -d | 代表要删除的分支名称 |
| 给当前提交新建一个标签名字 | git tag | 代表标签名字 |
| 修改和发布 | ||
| 列出所有当前配置的远程分支 | git remote -v | |
| 显示某远程分支的信息 | git remote show | 远程分支的名称 |
| 添加新的远程仓库,命名为 shortname | git remote add | 仓库名称, 仓库地址 |
| 更新远程跟踪分支 | git fetch | 远程分支的名称,从一个或多个其他存储库中获取分支和 / 或标签 (统称为“引用”) 以及完成其历史所必需的对象。 远程跟踪分支已更新(Git 术语叫做 commit),需要将这些更新取回本地,这时就要用到 git fetch 命令。 |
| 删除一个远程分支 | git branch -dr <remote/branch> | <remote/branch > 远程分支名称 |
| 推送指定标签 | git push --tags | |
| 更新远程分支中的内容到本地 | git pull | git pull 命令的作用是:取回远程主机某个分支的更新,再与本地的指定分支合并。 远程主机名, 分支名称 |
| 推送本地更改到远程分支 | git push | 远程主机名, 分支名称 |
| 分支合并 | ||
| 合并指定分支到当前分支 | git merge | 指定分支名称 |
| 合并指定分支到当前分支 | git rebase | 指定分支名称,不要重新设置已发布的提交 |
| 中止合并 | git rebase --abort | |
| 在解决冲突后继续合并 | git rebase --continue | |
| 使用配置的合并工具来解决冲突 | git mergetool | |
| 添加文件到暂存区 | git add | 使用编辑器手动解决冲突,(解析后)将文件标记为已解析 |
| 删除文件 | git rm | 使用编辑器手动解决冲突,(解析后)将文件标记为已解析 |
| 撤销 | ||
| 撤销当前工作目录下的所有更改 | git reset --hard HEAD | |
| 撤销指定文件的本地更改 | git checkout HEAD | |
| 恢复一个提交(通过提供一个新的提交产生相反的变化) | git revert | |
| 恢复到指定的提交 | git reset --hard | |
| 将所有更改保存为非阶段化更改 | git reset | |
| 保留未提交的本地更改 | git reset --keep |
使用 Git 相关注意事项
- 提交相关更改:
一个提交应该是一个相关更改的包装。例如,修复两个不同的错误应产生两个单独的提交。小型提交使其他开发人员更容易理解更改,并在出现问题时将其回滚。借助暂存区以及仅暂存部分文件的功能,Git 可以轻松创建非常精细的提交。
- 提交之前先测试代码
在提交时抵制一些你认为已完成的诱惑。对其进行彻底的测试,以确保它确实完成并且没有副作用(据人们所知)。虽然可以接受自己在本地存储库中提交的东西,但是在与他人推送 / 共享代码时,测试代码就显得尤为重要。
- 使用分支
分支是 Git 最强大的功能之一,这并不是偶然的:从一开始,快速简便的分支就成为了中心需求。分支是完美的工具,可帮助您避免混淆不同的开发路线。您应该在开发工作流程中广泛使用分支:有关新功能,错误修复,想法等……
- 经常提交
提交通常会使您的提交变小,同时,帮助您仅提交相关的更改。而且,它使您可以更频繁地与他人共享代码。这样,每个人都可以更轻松地定期集成更改,避免合并冲突。相比之下,大型提交很少且很少共享,使解决冲突变得困难。
- 写好提交时的注释信息
首先以简短的更改摘要(最多 50 个字符)作为您的信息。通过包含空白行将其与后续正文分开。您的注释正文应提供以下问题的详细答案:进行更改的动机是什么? 它与以前的实现有何不同?使用现在时态的命令(“ change”,而不是 “ changed” 或“ changes”)与 git merge 等命令生成的消息保持一致。
- 在工作流程上达成一致
Git 允许您从许多不同的工作流程中进行选择:长期运行的分支,主分支,合并或变基,git-flow… 您选择哪种取决于两个因素:您的项目,整体开发和部署工作流程,以及(也许最重要的)您和您队友的个人喜好。无论您选择哪种工作流程,只要确保在每个人遵循的工作流程一致即可。
- 不要提交未完成的工作
您只应在代码完成后提交代码。这并不意味着您在提交之前必须先完成一个完整的大型功能。恰恰相反:将功能的实现分成逻辑块,并记住提早并经常提交。但是,不要只想着在一天结束离开办公室时就在仓库中存放一些东西。如果您只是因为需要干净的工作副本(来切换分支,同步更改等)而打算提交,请考虑改用 Git 的 “隐藏” 功能。
- 版本控制不是备份系统
将文件备份到远程服务器上是拥有版本控制系统的一个很好的副作用。但是,您不应像将其用作备份系统那样使用 VCS。在进行版本控制时,您应该注意语义上的提交(请参见“相关更改”)- 您不应该只是在文件中塞入。
- 帮助和文档
在命令行获取帮助 $ git help <command>
- 免费的在线资源
http://www.gti-tower.com/learn
http://rogerdudler.github.io/git-guide/
http://www.git-scm.org/