学习时间:2018年12月7日~2019年1月6日 参考视频:深入掌握Git与实战开发

  • 直接记录快照,而非差异比较
  • 文件的三种状态
    已修改(modified)| 已暂存(staged)| 已提交(committed)
  • windows下换行符问题bash git config --global core.autocrlf false

Git常用命令

1. 获得版本库

Linux 下查看 git 安装目录which git

  1. git init # 初始化版本库
  2. git clone <url> # 克隆版本库

2. 版本管理

  1. git add . # 将已修改或新添加的文件加入到暂存区中(不包括被删除的文件)
  2. git add -u # git add --update 包括修改或者删除的文件
  3. git add -A # git add --all 等价于上面2个,即修改/删除/添加都会被加入到暂存区
  4. git add filename # 将已修改的文件加入到暂存区中,支持正则表达式
  5. git checkout -- filename # 将工作区中的修改丢弃掉(相对于最后一次保存在暂存区中的文件)
  6. git reset HEAD filename # 将添加到暂存区的内容移除到工作区
  7. git rm --cached filename # 将文件从暂存区删除
  8. git commit # 将暂存区的所有文件提交到版本库中
  9. git commit -m "注释" # 提交时直接增加注释信息
  10. git commit -am "注释" # 将修改的文件(之前纳入到版本库中)加入到暂存区并提交
  11. git rm filename # 删除文件,并纳入到暂存区
  12. # ------------ 等价于以下2步 ------------ #
  13. rm filename # 删除文件
  14. git add filename # 将删除操作加入到暂存区
  15. # ------------------------------------- #
  16. git commit -m "注释" # 提交删除操作
  17. git reset HEAD filename # 将待删除的文件从暂存区恢复到工作区
  18. git checkout -- filename # 将工作区中的修改丢弃掉
  19. git mv oldfilename newfilename # 重命名文件,并并纳入到暂存区
  20. # ------------ 等价于以下2步 ------------ #
  21. mv oldfilename newfilename # 删除文件
  22. git add oldfilename newfilename # 将删除操作加入到暂存区,或者执行"git add ."
  23. # ------------------------------------- #
  24. git commit -m "注释" # 提交
  25. git commit --amend -m "修改后的注释" # 修改上次提交的注释内容

3. 查看信息

  1. git status # 查看状态信息
  2. git log # 查看当前分支提交日志
  3. git log -n 3 # 查看最近3条提交日志
  4. git log --graph # 图形化查看
  5. git log --abbrev-commit # 简略显示commit ID
  6. git log --pretty=oneline # 以简略模式显示日志
  7. git log --pretty=format:"%h - %an, %ar : %s"
  8. git log origin/master # 查看远程master分支日志
  9. git log remotes/origin/master # 上面的完整写法
  10. git log refs/remotes/origin/master # 最完整的写法
  11. git reflog
  12. git blame filename # 显示文件的修改信息
  13. git help <命令> # 查看帮助信息
  14. git <命令> --help # 查看帮助信息
  15. man git <命令> # 查看帮助信息

4. 全局配置

  1. git config --global core.editor vim # 修改当前用户的默认编辑器为vim
  2. # 创建别名
  3. git config --global alias.br branch # 对branch增加别名br
  4. git config --global alias.co checkout # 对checkout增加别名co
  5. git config --global alias.unstage "reset HEAD" # 对reset HEAD增加别名unstage
  6. git config --global alias.s status # 对status增加别名s
  7. git config --global alias. '!gitk' # 运行外部命令gitk
  8. git config --global alias.logf "log --pretty=format:\"%h - %an, %ar : %s\""
  9. git config --global alias.logo "log --abbrev-commit --pretty=oneline"
  10. git config --global alias.logn "log -n"
  11. # 配置用户信息
  12. git config --local user.name "张三"
  13. git config --local user.email "test@test.com"
  14. git config --unset user.name # 删除user.name

user.name与user.email有3个地方可以设置:

  • /etc/gitconfig:整个计算机的name和email,实际开发几乎不会用
    git config --system
  • ~/.gitconfig:指定用户的name和email,很常用
    git config --global
  • .git/config:特定项目的name和email
    git config --local

5. 忽略文件

  1. echo "要忽略的文件" > .gitignore
  2. git add .
  3. git commit -m "提交.gitignore文件"

.gitignore支持正则表达式,git不会识别空文件夹

  • /*/.a:忽略一级子目录下的.a文件
  • /**/.a:忽略所有目录(包含根目录)下的.a文件
  • *.a:忽略所有目录中.a结尾的文件
  • !lib.a:保留lib.a文件
  • /TODO:仅忽略项目根目录下的TODO文件,XXX/TODO不会被忽略
  • build/:忽略build目录下的所有文件(夹)
  • doc/*.txt:忽略doc目录下的txt文件,但doc/XXX/*.txt不会被忽略

解决idea的.gitignore有时不起作用的问题 有时候,.gitignore会对部分文件/文件夹失效,大概原因是由于新创建的文件已经出现在git本地仓库的缓存,所以.gitignore就失效了。解决办法就是清空一下git仓库的缓存,重新提交一次就好了

  1. git rm -r --cached .
  2. git add .
  3. git commit -m 'update .gitignore'

Git分支

1. 显示当前分支

  1. git branch
  2. git branch -v # 显示注释信息

2. 创建新分支

  1. git branch new_branch

3. 切换分支

  1. git checkout new_branch
  2. git checkout - # 切换到上一个分支
  3. git checkout -b new_branch # 创建新分支并切换
  4. # checkout高级用法
  5. git checkout commit_id # 直接切换到某一次提交状态,HEAD会处于游离状态
  6. git commit -am "注释" # 修改文件后需要提交,提交将不处于任何分支上
  7. git branch my_branch commit_id # 将该次提交绑定到新创建的分支上

4. 删除分支

不能删除当前所处分支

  1. git branch -d new_branch # 只能删除已合并的分支
  2. git branch -D new_branch # 未合并的分支可以用此命令删除

5. 合并分支

  1. git merge new_branch # 将分支new_branch合并到当前分支
  2. git merge --no-ff new_branch # 合并时禁用fast-forward

fast-forward模式进行分支合并时不会增加新的commit(直接用分支的commit) 如果出现冲突,需要手动修改文件

6. 分支重命名

  1. git branch -m new_branch dev

版本回退

1. 回退到上一版本

  1. git reset --hard HEAD^ # 回退到上一版本
  2. git reset --hard HEAD^^ # 回退到上一版本的上一版本
  3. git reset --hard HEAD~n # 回退到第n个版本

2. 进入任何版本

  1. git reflog # 获取所有的commit_id(git log只会显示当前的commit_id)
  2. git reset --hard commit_id # 进入任一版本

3. 保存当前分支的状态

  1. git stash # 保存当前分支的状态
  2. git stash save "注释" # 保存当前分支的状态,并添加注释
  3. git stash list # 显示所有保存的状态
  4. git stash pop # 恢复上一个状态,并删除保存的状态
  5. git stash apply # 应用上一个状态,不删除保存的状态
  6. git stash apply stash@{n} # 应用第n个状态,不删除保存的状态

标签管理

1. 创建标签

  1. git tag v1.0 # 轻量级标签
  2. git tag -a v1.0 -m "注释" # 带有备注的标签

2. 查看标签

  1. git tag
  2. git show v1.0

3. 查找标签

  1. git tag -l "v1.0"
  2. git tag -l "*2"
  3. git tag -l "v*"

4. 删除标签

  1. git tag -d v1.0

5. 标签推送到远程

git push默认不会推送标签

  1. git push origin v1.0
  2. git push origin v2.0 v3.0 # 推送多个标签
  3. git push origin --tags # 一次性全部推送到远程

6. 标签拉取到本地

git pull会拉取标签

7. 删除远程标签

  1. # 方式一
  2. git push origin :refs/tags/v6.0
  3. # 方式二
  4. git push origin --delete tag v5.0
  5. # 以上两种方法执行git pull操作后都不会影响本地已有标签

差异比较

1. linux内置diff

  1. diff a b # 比较文件a、b的区别
  2. diff -u a b #

2. 暂存区与工作区之间的差别

  1. git diff # 暂存区为原文件,工作区为目标文件

3. 版本库与工作区之间的差别

  1. git diff HEAD # 比较最新提交与工作区之间的差别
  2. git diff commit_id # 比较指定提交与工作区之间的差别

4. 版本库与暂存区之间的差别

  1. git diff --cached # 比较最新提交与版本库之间的差别
  2. git diff --cached commit_id # 比较指定提交与暂存区之间的差别

远程与GitHub

基于Git分支的开发模型

  • develop分支:用于开发者进行开发,频繁变化
  • test分支:供测试与产品人员使用的一个分支,变换不是特别频繁
  • master分支:用于生产发布,变换非常不频繁
  • bugfix分支:用于紧急修复bug

1. 概念

  • push:推送> git push完整写法:git push origin src:dest
  • pull:拉取,同时会执行合并。pull == fetch + merge> git pull完整写法:git pull origin src:dest

2. 注册GitHub

3. 已有仓库关联GitHub

  • 使用HTTPS方式(不推荐)```shell git remote add origin https://github.com/LoveShes/gitlecture.git # 将origin指代远程地址 git push -u origin master # 将本地当前分支与远程master分支关联

    输入GitHub的用户名和密码

    ```

  • 使用SSH方式【推荐】```shell

    1.首先在本地生成密钥对

    ssh-keygen # 在~/.ssh/目录生成密钥对

    2.将id_rsa.pub内的内容添加到GitHub中(仓库级别或者账户级别)

    仓库 -> Settings -> Deploy keys -> Add deploy key,并勾选Allow write access

    3.进行关联

    git remote add origin git@github.com:LoveShes/gitlecture.git git push -u origin master

    1. > 如果设置公钥后每次push还需要输入用户名和密码,可以使用下面的命令缓存账户信息:
    2. > ```shell
    3. git config credential.helper store

4. 推送到远程分支

  1. git push
  2. # push有两种模式
  3. git config --global push.default matching # 推送到远程同名分支
  4. git config --global push.default simple # 推送到用于更新本地分支的远程远程分支

5. 查看远程信息

  1. git remote show origin # 查看远程仓库信息
  2. git branch -av # 查看所有分支(包括远程分支)信息,显示缩略commit_id和注释

6. 本地分支管理远程分支

  • 远程没有同名分支时,直接push会报错shell git push --set-upstream origin develop # 远程创建同名分支,并推送过去

  • 另一个开发者获取develop分支shell git pull # 拉取所有分支 git checkout -b develop origin/develop # 创建本地develop分支,并关联远程分支 git checkout --track origin/develop # # 创建本地同名分支,并关联远程分支

7. 删除远程分支

  1. # 第一种方法
  2. git push origin :develop # 删除远程develop分支
  3. # 第二种方法
  4. git push origin --delete develop

8. 本地分支与远程分支不同名

  1. git push --set-upstream origin HEAD:develop2 # 远程创建develop2分支,并将当前分支推送过去

远程协作

1. 克隆仓库

  1. # 将远程仓库所有分支拉取到本地,默认进入master分支,以远程仓库名作为本地仓库名
  2. git clone git@github.com:LoveShes/gitlecture.git
  3. # 克隆仓库,以mygit2作为本地仓库名
  4. git clone git@github.com:LoveShes/gitlecture.git mygit2
  5. # 修改user.name和user.email为当前用户
  6. git config --local user.name "vMayuyu"
  7. git config --local user.email "vMayuyu@qq.com"

2. 推送仓库

  1. git remote show origin # 显示远程仓库信息
  2. git pull # push之前需要先pull远程仓库
  3. # --------- pull == fetch + merge --------- #
  4. git fetch
  5. git merge origin/master
  6. # ----------------------------------------- #
  7. git push # 推送到远程
  8. # 如果发生冲突
  9. # 1.手动修改冲突文件
  10. # 2.提示系统解决了冲突
  11. git add filename
  12. # 3.提交
  13. git commit -m "注释"

Git图形界面

1. gitk

  1. apt install gitk # 安装gitk
  2. gitk # 启动gitk

2. git gui

  1. apt install git-gui # 安装git gui
  2. git gui # 启动git gui

3. GitHub Desktop

官网下载安装,不包含Linux版本

Git 高级应用

1. Git refspec

.git/refs/下的文件里面都对应着commit ID,refs目录结构如下:

  1. refs/
  2. ├─ heads
  3. ├── master (内容为commit ID)
  4. └── ...
  5. ├─ remotes
  6. ├──master (内容为commit ID)
  7. └── ...
  8. └─ tags
  9. ├── v1.0 (内容为commit ID)
  10. └── ...

2. Git gc

执行git gc后,refs文件夹下的内容会被压缩到packed-refs文件夹下。

object/pack目录:idx文件、pack文件、压缩之后的索引。

实际开发用的较少

3. Git 裸库

不包含工作区的库,在文件夹内使用命令git init --bare创建。

4. Git submodule

4.1 创建项目

用于在一个项目内引用另一个项目,仅能将子模块中的更改同步到父模块,不支持双向更改。

先在GitHub上创建2个项目,git_parent与git_child,再在本地执行如下操作:

  1. # git_parent项目
  2. mkdir git_parent && cd git_parent
  3. git init
  4. git config --local user.name '张三'
  5. git config --local user.email 'zhang@qq.com'
  6. echo 'parent' >> parent.txt
  7. git add .
  8. git commit -m 'initial commmit'
  9. git remote add origin git@github.com:LoveShes/git_parent.git
  10. git remote show origin # 显示远程分支信息
  11. git push --set-upstream origin master
  1. # git_child项目
  2. mkdir git_child && cd git_child
  3. git init
  4. git config --local user.name '李四'
  5. git config --local user.email 'li@qq.com'
  6. echo 'child' >> child.txt
  7. git add .
  8. git commit -m 'initial commmit'
  9. git remote add origin git@github.com:LoveShes/git_child.git
  10. git remote show origin # 显示远程分支信息
  11. git push --set-upstream origin master
  12. echo 'hello' >> hello.txt
  13. git add hello.txt
  14. git commit -m 'add hello.txt'
  15. git push

再在git_parent项目中执行submodule操作

  1. git submodule add git@github.com:LoveShes/git_child.git mymodule # mymodule之前不能存在
  2. git add .
  3. git commit -m 'add submodule'
  4. git push

4.2 更新单个子模块

  1. cd mymodule # 进入子模块目录
  2. git pull

4.3 更新所有子模块

  1. # 需在父模块中执行
  2. git submodule foreach git pull

4.4 克隆包含子模块的父模块

  1. # 手动克隆子模块
  2. git clone git@github.com:LoveShes/git_parent.git git_parent2
  3. git submodule init
  4. git submodule update --recursive
  5. # 自动克隆子模块
  6. git clone git@github.com:LoveShes/git_parent.git git_parent3 --recursive

4.5 删除子模块

  1. git rm --cached mymodule
  2. rm -rf mymodule
  3. rm .gitmodules
  4. git add .
  5. git commit -m 'remove submodule'
  6. git push

4. Git subtree

在父项目中可以双向修改子项目的内容,推荐使用

4.1 创建项目

先在GitHub上创建2个项目,git_subtree_parent与git_subtree_child,再在本地执行如下操作:

  1. # git_subtree_parent项目
  2. mkdir git_subtree_parent && cd git_subtree_parent
  3. git init
  4. git config --local user.name '张三'
  5. git config --local user.email 'zhang@qq.com'
  6. echo 'parent' >> parent.txt
  7. git add .
  8. git commit -m 'initial commmit'
  9. git remote add origin git@github.com:LoveShes/git_subtree_parent.git
  10. git remote show origin # 显示远程分支信息
  11. git push --set-upstream origin master
  1. # git_subtree_child项目
  2. mkdir git_subtree_child && cd git_subtree_child
  3. git init
  4. git config --local user.name '李四'
  5. git config --local user.email 'li@qq.com'
  6. echo 'child' >> child.txt
  7. git add .
  8. git commit -m 'initial commmit'
  9. git remote add origin git@github.com:LoveShes/git_subtree_child.git
  10. git remote show origin # 显示远程分支信息
  11. git push --set-upstream origin master

再在git_subtree_parent项目中执行subtree操作:

# 关联远程子项目
git remote add subtree-origin git@github.com:LoveShes/git_subtree_child.git
# 添加远程子项目
git subtree add --prefix=subtree subtree-origin master --squash
# 注意,squash参数会合并远程提交历史成一次再拉取到本地
# squash参数要么不使用,要么一直使用
git push

4.2 更新单个子模块

git subtree pull --prefix=subtree subtree-origin master --squash
git push

4.3 在父项目中更改子模块并推送到子模块

git subtree push --prefix=subtree subtree-origin master --squash

git submodule与git subtree

  • git submodule会在当前目录下建立一个指向子项目的指针,可以更新所有子模块。
  • git subtree会在当前目录下建立一个包含完整子项目的文件夹,只能一个一个更新。

但是,两者在本地都会建立一个完整的文件夹。

5. Git cherry-pick

5.1 将当前分支上的修改复制到其它分支上

# 之前在其它分支上开发,先切换到master分支上
git checkout master
git cherry-pick <commitID>
# 注意,跳过中间的提交需要手动解决冲突

5.2 删除当前分支上的一些修改

git checkout <commitID>
git branch -D develop    # 强制删除develop分支
git checkout -b develop    # 基于当前commitID创建新的develop分支

6. Git rebase

用于改变分支的根基,功能与merge相似,但工作方式不同

6.1 变基

git checkout develop    # 切换到develop分支
git rebase master    # 将develop分支分支的基础变为master

执行之后会修改提交历史,整个commit会变成一条直线

6.2 解决冲突

出现冲突后,手动解决冲突,使用git add添加后,执行

git rebase --continue

6.3 终止rebase

git rebase --abort    # 会恢复到rebase开始前的状态

注意,不要对master分支进行rebase操作,并且执行rebase操作的分支都应该是没有推送到远程的本地分支。