Ubuntu---Git
本篇文章简单总结了常用 Git 的使用
前言
设置用户信息
1, Git 是分布式的 SSH 代码管理工具,远程的代码管理是基于 SSH 的,所以要使用远程的 Git 则需要 SSH 的配置。
step 1: 设置 Git 用户的 name 和 email:
$ git config --global user.name "peng.zhang" $ git config --global user.email "peng.zhang@yeah.net"
生成 SSH 密钥对
step 2: 生成 ssh 密钥:
# Ubuntu 默认已安装了 git 和 ssh # 进入家目录下的隐藏文件夹 .ssh 下 $ cd ~/.ssh # 生成公私钥对
# 生成的公私钥对名称分别为:公钥:id_rsa 私钥:id_rsa.pub,如果不是,则用 mv 命令重命名即可 $ ssh-keygen
加入 SSH 公钥到远端仓库(Repo)
step 3: 登录 github 账户,然后把生成的公钥加入到 github 账户下的 SSH 公钥列表中,即可对github 进行 ssh 访问了
# 测试是否能连接到 git 账户
$ git clone git@github.com:ssezhangpeng/script-to-chouti.git
git 操作
初始化 git 仓库
method1:自己在本地创建一个空仓库
method2:从远程 clone 一个仓库到本地
# 创建一个空文件夹作为仓库 $ mkdir git # 进入空文件夹,初始化该仓库 $ cd git $ git init
加入文件到工作区(Work Directory),此时未 add 进 暂存区(Stage)
# 加入新文件到仓库 <~git>$ mkdir dir1 dir2 dir3 <~git>$ vim dir1/main.cpp <~git>$ vim dir2/logic.cpp <~git>$ vim dir3/logs.txt
# 此时用 git status 看一下,发现是
# status: Untracked
add 文件到暂存区(Stage)
# add 文件到 Stage 区 # . 表示本目录下的所有文件 <~git>$ git add . # 此时用 git status 看一下,发现是 # status: changes to be commit <~git>$ git status # 此时如果想要撤回 add 的文件 # stage---> unstage <~git>$ git rm --cached file-name
commit 文件到本地仓库分支(Repo)
$ 提交暂存区所有文件到本地仓库 <~git>$ git commit -m "version 0.1"此时如果想要撤回 add 的文件 # 此时用 git status 看一下,发现是 # status: nothing to commit, working tree clean # 此时如果想要撤回 commit 的文件 暂时还没整理好,待续...
commit 后继续修改原文件
# 继续开发新功能,修改了 dir1/main.cpp <~git>$ vim dir1/main.cpp # 此时用 git status 看一下,发现是 # status: modified: dir1/main.cpp # ===============如果想要更新该文件=============== <~git>$ git add dir1/main.cpp
# add 之后如果想要撤销此次 add 进的文件
<~git>$ git reset HEAD dir1/main.cpp
# 此时可以用 git status 看一下,发现回到了修改后未 add 的状态,此时可以用 add / checkout -- file-name 来决定是否要更新/忽略此次修改
# ====================END======================
# 如果想要忽略此次修改,将修改后的文件恢复到未修改之前的状态,像修改文件后没有保存直接退出的操作
<~git>$ git checkout -- dir1/main.cpp
# 如果想要看一下修改的内容是什么
# 查看当前工作区和暂存区的不同,当然 git diff 还有其它功能,后面有补充
<~git>$ git diff
补充:时刻用 git diff 来查看不同之处
# git diff 的强大之处 # ==========[1]工作区 VS 暂存区===========
# 如果还没 add 进暂存区,则查看文件自身修改前后的差别 $ git diff <file-name>
# 查看与工作区与另一分支的区别
$ git diff <branch> <file-name>
# ==========[2]暂存区 VS Git仓库==========
# 查看已经 add 进暂存区但是尚未 commit 的内容同最新一次 commit 的内容的差异
# git diff --cached <file-name>
# 上述可以指定 commit 的版本
$ git diff --cached <commit-ID> <file-name>
# ==========[3]工作目录 VS Git仓库==========
# 查看工作目录同 Git仓库指定的 commit 的内容差异,当 commit-ID = HEAD 时,查看同最近一次 commit 的内容差异
$ git diff <commit-ID> <file-name>
# ==========[4]Git 仓库 VS Git仓库==========
# Git 库任意两次 commit 的差别
$ git diff <commit-ID> <commit-ID>
注1:以上命令均可以不指定 <file-name>,此时对全部文件操作
注2:以上涉及和 Git 仓库进行对比的,均可以指定 commit 的版本(HEAD、HEAD^ ,HEAD~100 等命令可指定)
版本回退
=======前言=========== # 用来显示从近到远的 commit 日志 $ git log # 用来记录每一次命令 $ git reflog # 移动到任意一个版本[版本穿梭] git reset --hard <commit-ID> ========版本回退======== # 想回到上一个版本 $ git reset --hard HEAD^ # 回到未来的版本:通过 git reflog 找到未来的 commit-ID $ git reset --hard <commit-ID>
删除文件
# 删除文件也是一个修改操作 # 我们先加一个文件 $ git add test.txt $ git commit -m "add test.txt" #============删除文件======== # 一般情况下,通常在工作区直接把文件删除了 $ rm -r test.txt # 此时 Git 知道你删除了文件,此时工作区和版本库就不一致了 # 用 git status 看一下: Changes not staged for commit [delete:test.txt] # 1,如果你确实要从版本库中删除该文件 $ git rm -r test.txt $ git commit -m "remove test.txt" # 2,如果你是删除错了,因为版本库中还有,所以可以恢复到版本库中的最新版本 $ git checkout -- test.txt
分支操作
#============创建分支============ # 在本地 master分支下创建并切换到新分支 dev [note:如果想在dev分支下创建新分支,首先切换到 dev 分支,然后再执行下述命令] $ git checkout -b dev # 等价于下面两条命令 $ git branch dev $ git checkout dev # 创建远程分支 # 当我们在本地创建了新分支之后,直接推送到远端,此时远端就会生成一个同名分支 $ git push origin dev #============删除分支============ # 删除本地分支 dev $ git branch -d dev
# 强制删除
$ git branch -D dev
# 删除远端分支 dev
$ git push origin --delete dev
注:删除该分支之前必须要切换到其它分支,然后再执行删除操作
合并操作
# 本地合并操作
# 合并指定分支到当前分支,当前分支在 master 上
$ git merge dev
解决冲突
# 当进行合并操作时,如果两个要合并的分支内容不同,则产生冲突,因为 git 当前分支不知 # 道我是继续保留我自己的分支,还是要换成指定分支的内容,需要用户手动解决冲突 # 例如 master 分支内容为: to test conflict! # feature1 分支内容为: TO TEST CONFLICT! # 我们想要把 dev 分支上的内容合并到 master 上 # 执行下面命令后会出现错误提示: Merge conflict in readme.txt(冲突文件名) $ git merge feature1 # 此时我们用 vim 打开冲突的文件,进行手动编辑决定保留哪一个 # 此时冲突文件中: # <<<<<<<<<HEAD :表示 master 冲突文件中不同的内容 # >>>>>>>>> dev : 表示指定合并的分支的冲突文件中不同的内容 # 并用 ==============来分割两个不同的分支的内容 # 解决冲突后可以重新进行合并操作,但是注意也要重新 add 文件 $ git add readme.txt $ git commit -m "fixed conflict"
此时查看分支的合并情况:
$ git log --graph --pretty=oneline --abbrev-commit
Rebase 合并提交历史记录
当多人在一个分支上协同工作时,很容易出现冲突。即使没有冲突,后 push 的同学不得不先 pull ,在本地合并,然后才能 push 成功。每次合并再 push 后,分支就会变成下图那样:
那能不能使 Git 的提交历史是一条干净的直线?当然可以,这就是 Rebase 的作用。
# 首先我们在本地分支 master 上添加新文件 helo.py,内容为 # Author:peng.zhang # 然后 add , commit $ git add hello.py $ git commit -m ".py file add Author" # 此时用 git status 看一下发现当前分支提前 origin/master 一个提交 # 然后我们在 github 上修改 README.md 文件,然后直接在页面上 commit,以此模拟其它同学的提交操作 # 然后我们推送我们的修改到远端 # 此时发生错误:updates were rejected because...,这说明有人先于我们推送了远程分支 $ git push origin master # 按照经验,先 pull 一下 # 提示:Merge made by the ‘recursive’ strategy # pull 中包含 merge 操作,没有发生冲突,因为我们只是增加了文件,没有修改其它相同文件 # 如果发生了冲突,则需要先 fetch 到本地,然后手动解决冲突,再执行 merge 操作 $ git pull
此时我们看一下 commit 历史记录:
$ git log --graph --pretty=oneline --abbrev-commit
可以看到,此时历史提交记录已经分叉了,我们用 rebase 试试看结果是什么?
$ git rebash
此时,我们再看一下历史提交记录
我们可以发现历史提交记录变成了一条直线,我们仔细看可以发现变化的是什么?其实就是 Git 把本地的提交移动了位置,相当于[先从本地执行 pull 操作,然后再在本地做修改,接着再提交修改]的顺序执行,把一个并行操作转换为一个顺序执行的操作,这样就不会发生任何冲突,所以也就不需要进行合并操作,即历史记录不会有分支。
开发过程中所用到的一些操作
git fetch
当我们在自己的分支进行开发时,别的同事可能已经对 master 分支进行过合并操作,我们需要取回远端 master 分支上我们本地没有的内容,然后再进行本地和远程分支的合并操作,之后 push 到远端。
# git fetch 可以从远端仓库获取到最新版本到本地 # 将远程主机的更新全部取回本地,git fetch 获取所有分支的更新 $ git fetch # 取回特定分支的更新,可以指定分支名称 $ git fetch origin master #当取回远程主机的更新以后,可以执行合并操作,在本地分支上合并远程分支,当然,此时可能会发生冲突,需要手动解决冲突 # 在当前分支上合并 origin/master $ git merge origin/master
注:具体图示见文末参考资料链接[1]
git stash
当我们在自己的分支进行开发工作时,此时有一个 Bug 需要我们紧急修复,我们没有办法先把我们的开发工作提交,因为该工作还没完成,这是我们就可以用打 stash 命令了。这条命令会把当前个工作现场“储存起来”,等以后恢复现场后继续工作。
# 存储现场 $ git stash # 此时用 git status 看一下工作区,发现是干净的 ========修复 Bug========= # 如果我们要在 master 分支上修复 issue $ git checkout master $ git checkout -b issue-101 $ vim bug-file 进行修复 $ git add bug-file $ git commit -m "fixed issue-101" # 切换回 master 分支,完成合并 $ git checkout master $ git merge --no-ff -m "merged bug fix issue-101" =======恢复现场======= # 查看保存的工作现场 $ git stash list #恢复现场方法1:该方法不会删除保存的工作现场,需要用 git stash drop 来删除 $ git stash apply # 恢复现场方法2:恢复的同时也把 stash 给删除了 $ git stash pop ===========多次 stash======== # 恢复的时候,先用 git stash list 查看,然后恢复指定的 stash $ git stash apply stash@{0}
===========清空 stash========
#如果有多个 stash 队列,下面命令会清空所有 stash 队列
$ git stash clear
多人协作
在工作中,我们都会往 master 和 dev 分支上推送各自的修改,当我们从远程库 clone 时,默认情况下,只能看到本地的 master 分支,当我们要在 dev 分支上进行开发时,就必须创建远程的 dev 分支到本地(现实情况是 master 是稳定分支,dev 是开发分支,我们再建立每个人自己的开发分支,比如我的分支名称为: from_zhangpeng,然后不断往 dev 分支上进行何合并,由管理员合并 dev 分支到 master 分支)
# 建立自己的分支 dev # 下行命令可以自动追踪 dev 分支[此时远程的 dev 分支必须已存在] $ git checkout -b dev origin/dev # 不自动追踪 dev 分支 $ git checkout -b dev # 不断进行修改,时不时把本地 dev 分支推送到远程 dev 分支 # 如果已建立追踪,则可以忽略分支名称 $ git push origin dev
git 相关配置
gitconfig 相关配置
# ===========配置用户名和密码============ $ git config --global user.name "peng.zhang" $ git config --global user.email "peng.zhang@yeah.net" # 如果希望在不同的项目中使用不同的 user.name 和 user.email,可在该项目中运行: $ git config user.name "peng.zhang" $ git config user.email "peng.zhang@yeah.net" # ===========配置默认编辑器============ $ git config --global core.editor vim # ===========配置默认比较工具============ $ git config --global merge.tool vimdiff # ===========检查配置============ $ git config --list # ===========添加配置项============ # 默认为 local 范围修改 $ git config [--local | --global | --system ] --add site.name yiibai # ===========删除配置项============ $ git config --local --unset set.name # ===========获取帮助============ $ git help config
参考资料
[1] https://git-scm.com/book/zh-tw/v1
[2] https://www.liaoxuefeng.com/wiki/896043488029600
[3] https://www.yiibai.com/git/git-quick-start.html