Git看这一篇就够了

Lou.Chen
大约 21 分钟

一、Git和SVN的区别

1、SVN

SVN是集中式版本控制系统版本库是集中放在中央服务器的,如果中央服务器挂了,那么我们的历史版本则只有当前的历史版本,即可能发生单点故障

集中式的版本控制系统都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。

优点:每个人都可以一定程度上看到项目中的其他人正在做些什么。而管理员也可以轻松掌控每个开发者的权限。

  • 缺点:中央服务器的单点故障。

若是宕机一小时,那么在这一小时内,谁都无法提交更新、还原、对比等,也就无法协同工作。如果中央服务器的磁盘发生故障,并且没做过备份或者备份得不够及时的话,还会有丢失数据的风险。最坏的情况是彻底丢失整个项目的所有历史更改记录,被客户端提取出来的某些快照数据除外,但这样的话依然是个问题,你不能保证所有的数据都已经有人提取出来。

2、Git

Git是分布式版本控制系统每个机器都保存一份版本库,能够避免单点故障,如果中央服务器挂了,那么我们的本机也可以恢复历史版本

在分布式版本控制系统中,客户端并不只提取最新版本的文件快照,而是把原始的代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。这类系统都可以指定和若干不同的远端代码仓库进行交互。即此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。你可以根据需要设定不同的协作流程。

  • 优点
    • 大部分操作在本地完成,不需要联网
    • 完整性保证
    • 尽可能添加数据而不是删除或修改数据
    • 分支操作非常快捷流畅
    • 与 Linux 命令全面兼容

二、Git结构(工作区&暂存区&本地库)

三、Git和代码托管中心

1、局域网

  • GitLab

2、外网

  • GitHub
  • 码云

四、本地库和远程库

1、团队内部协作

2、跨团队协作

五、Git基本命令

1、本地库初始化

1)git init

初始化本地仓库

2、设置签名

1)形式

  • 只是标识作用,无实际意义,在提交推送时会显示【用户名】

用户名tom Email

地址:421192425@qq.com

2)作用

  • 区分不同开发人员的身份

  • 这里设置的签名和登录远程库(代码托管中心)的账号、密码没有任何关系。

3)命名

  • **项目级别/仓库级别:**仅在当前本地库范围内(当前.git下的项目)有效

    • git config user.name louchen
      
    • git config user.email 421192425@qq.com 
      
    • 查看签名保存位置

      • cat .git/config 
        
  • **系统用户级别:**登录当前操作系统的用户范围

    • git config --global user.name louchen_glb
      
    • git config --global user.email 421192425_glb@qq.com
      
    • 查看系统签名保存位置

  • 级别优先级

    • 就近原则:项目级别优先于系统用户级别,二者都有时采用项目级别 的签名
    • 如果只有系统用户级别的签名,就以系统用户级别的签名为准
    • 二者都没有不允许

3、基本操作

1)查看状态
  • git status 查看工作区、暂存区状态

几个状态的示例:

  • - 表示还在工作区,既没有`add`、也没有`commit`,不受版本控制
  • - 表示已经添加到暂存区,已经`add`,没有`commit`
  • - 所有内容已经提交到本地库,已经`add`和`commit`
2)添加

将工作区的“新建/修改”添加到暂存区

  • git add [filename] 添加指定文件
  • git add . 添加所有文件
3)提交

将暂存区的内容提交到本地库

  • git commit -m "commitmessage" [filename] 提交指定文件到本地库
  • git commit -m "commitmessage" 提交所有文件都本地库
4)查看历史记录
  • git log

    • 多屏显示控制方式:

      空格向下翻页, b 向上翻页, q 退出

  • git log -pretty=oneline

  • git log --oneline

  • 只能够显示过去的版本,不能显示回退后的未来的版本

  • git reflog

    • HEAD@
  • git loggit reflog 区别

    • git reflog 命令可以显示整个本地仓库的 commit , 包括所有 branch 的 commit , 甚至包括已经撤销的 commit , 只要 HEAD 发生了变化, 就会在 reflog 里面看得到
    • git log 只显示当前分支的 commit ,并且不显示删除掉的 commit
5)版本的前进与后退
  • HEAD指针

    • HEAD 是当前分支引用的指针,它总是指向某次commit,默认是上一次的commit。 这表示 HEAD 将是下一次提交的父结点。 通常,可以把 HEAD 看做你的上一次提交的快照。当然HEAD的指向是可以改变的,比如你提交了commit,切换了仓库,分支,或者回滚了版本,切换了tag等。
  • 基于索引值操作[推荐]

    • git reset --hard [局部索引值]

    • git reset --hard c826707

    • 版本的前进和后退都适用

  • 使用^符号:只能后退

    • git reset --hard HEAD^
      • 注:一个^表示后退一步,n 个表示后退 n 步
  • 使用~符号:只能后退

    • git reset --hard HEAD~n
      • 注:表示后退 n 步
①reset 命令的三个参数对比(版本回退后对应的状态)

git reset --hard/soft/mixed [局部索引值]

  • --soft

  • git reset --soft [版本号]/HEAD

    • 回退到HEAD版本或者指定版本号
    • 重置本地库
      • 回退的版本相当于只add了并没有commit
  • --mixed

    • git reset --mixed [版本号]/HEAD
    • 重置暂存区
    • 重置本地库
    • 回退到的版本没有add也没用commit
  • --hard

    • git reset --hard [版本号]/HEAD
    • 重置工作区
    • 重置暂存区
    • 重置本地库
      • 回退到的指定版本的之后的所有操作都重置(即该版本提交的初始状态),受版本库控制的内容才会被重置
6)找回删除/修改之前的文件
  • 前提:删除和修改前,该文件都受版本控制器控制

  • 回退到文件存在的版本即可 :git reset --hard [回退的版本号]/HEAD

    • 删除操作已经提交到本地库:指针位置指向历史记录

    • 删除操作尚未提交到本地库:指针位置使用 HEAD

      • git reset --hard HEAD 回退到HEAD指针的位置(文件存在的位置)
7)文件的比较
  • git diff [文件名]

    • 将工作区中的文件和暂存区进行比较
  • git diff [本地库中历史版本] [文件名]

    • git diff HEAD^ a.txt 和HEAD指向的上一个版本比较
    • git diff HEAD a.txt 和HEAD指向的版本比较
    • git diff HEAD 比较所有文件
    • 将工作区中的文件和本地库历史记录比较

4、分支管理

1)创建分支

git branch [分支名称]

2)查看当前本地所有分支

git branch

  • git branch -[参数]
    • -a 查看所有远端分支和本地分支
    • 使用 git branch -help 查看更多参数用法
  • *代表当前处于的分支
3)切换分支

git checkout [分支名称]

4)创建并切换分支

git checkout -b [分支名称]

5)合并分支

当前存在分支hot_fix和分支master, 分支都存在文件a.txt

  • ①切换到修改的分支hot_fix,对文件进行修改操作,并add和commit

  • ②切换到需要合并的分支master上(将hot_fix分支修改的文件加到master分支上)

    • 将某分支的内容合并加入到当前分支,并使其当前分支作为主分支

    • git merge [修改内容的分支]

6)解决冲突

**注意:**两个分支必须都进行修改和提交后,合并才有效,否则会直接覆盖当前的分支内容

  • ①分支hot_fixa.txt内容进行修改,add和commit

  • ②切换到主分支mastera.txt内容进行修改,add和commit

  • ③切换到hot_fix分支,合并master分支

    • git merge master

    • 产生冲突

  • ④到文件中解决冲突再合并

    • git add . 加入到暂存区
    • git commit -m 'message' 提交

六、远程库创建

1、创建远程库

2、初始化本地库

3、将本地库和远程库关联

  • git remote -v 查看远程库地址

  • git remote add [别名] [远程地址]

  • git push -u origin master

    -u参数可以在推送的同时,将 origin 仓库的 master 分支设置为本地仓库当前分支的 upstream(上游)。

    添加了这个参数,将来运行 git pull 命令从远程仓库获取内容时,本地仓库的这个分支就可以直接从 origin 的 master 分支获取内容,省去了另外添加参数的麻烦。这个参数也只用在第一次 push 时加上,以后直接运行 git push 命令即可。

  • git push [别名] [分支名]

    • git push origin master 推送到远端地址别名为originmaster分支上(远端自动创建master分支)
  • 拉取远端指定分支
    • git branch b1 origin/b1
      • 创建本地分支b1,并将远端分支关联到本地b1分支,不切换到b1分支
    • git checkout -b b1 origin/b1
      • 创建本地分支b1,并将远端分支关联到本地b1分支,并切换到b1分支

4、克隆操作

git clone [url]

克隆会直接帮我们完成以下操作:

  • 完整的把远程库下载到本地

  • 创建 origin 远程地址别名

  • 初始化本地库

5、团队成员邀请

当我们另一个小伙伴clone下来的项目时无法推送的,只能访问。需要该仓库创建人的邀请。

若在本机需要删除之前推送过的github的账户和密码,可在凭据管理器中删除

1)github邀请成员可推送
  • 被邀请人同意邀请
  • 我们可以看到该成员已经加入该仓库的管理

****

6、拉取

git pull=git fetch+git merge

git fetch [远程库地址别名] [远程分支名]

git merge [远程库地址别名/远程分支名]

git pull [远程库地址别名] [远程分支名]

7、冲突解决

  • 如果不是基于 GitHub 远程库的最新版所做的修改,不能推送,必须先拉取。
    • 即我们提交代码前,最好先 git pull拉取最新的代码
  • 拉取下来后如果进入冲突状态,则按照“分支冲突解决”操作解决即可。

8、使用SSH免密推送

ssh-keygen -t rsa -C [github账户]

  • 以下操作默认即可,直接回车
  • 赋值生成的秘钥到github中
  • 项目使用

七、IDEA使用Git

1、初始化本地版本库

2、创建ignore文件

两种方式

①通过插件ignore在项目根目录下生成或手动创建 .gitignore文件

②创建.git/info/exclude文件,并打开文件

# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
#忽略的文件
.idea
*.iml

3、版本的回退

**注意:**在idea中只能进行版本的回退,若回退之后的版本想前进,那么只能用命令行操作

①第一种方式:直接回退

  • 查看历史版本,并选择回退的版本

  • 回退成功

②第二种方式:使用版本号回退

  • 回退成功

4、创建和合并分支

ctrl+shift+` 打开Git Branches界面

①新建分支

在dev分支上新增一些内容并提交

②切换到master分支,并合并dev分支

  • 我们发现在dev分支提交的一些内容已经合并到master分支上

5、合并解决冲突

1)主干分支添加和提交的内容

2)dev分支添加和提交的内容

3)切换到master主分支,合并dev分支

我们发现在第13行master分支和dev分支有不同的内容,合并即产生冲突

  • 选择全部保留
  • 查看历史结果

6、关联github远程仓库

1)创建远程仓库并赋值仓库的地址

2)项目推送远端并关联地址

  • 推送
  • 若推送没有账户,则添加账户

    • ①添加github账户
    • ②添加gitee账户
      • 搜索安装gitee插件
  • 推送成功

7、拉取并解决冲突

1)修改远程仓库内容并提交保存

2)本地仓库内容(需要更新一些内容后提交再合并其他分支,否则会覆盖)

  • 拉取(手动merge)

    • ①第一种方式

    • ②第二种方式

  • 解决本地master分支合并远端的origin的master分支的冲突

  • 手动解决冲突
  • 将合并的后的代码提交到远端
    • 本地内容
    • 远端内容
注意:merge/pull/push的覆盖问题
  • git是比较时间戳的, 即某一个分支commit提交后,该分支push/pull/merge到其他比该commit提交时间晚的分支那么内容都会直接覆盖,不会产生冲突。
  • 结论:
    • 新的分支commit提交后的push/pull/merge的操作内容总是会覆盖旧的分支的内容。不会产生任何冲突
    • 旧的分支commit提交后的push/pull/merge的操作的内容到新的提交的分支的内容,若有冲突,则会产生冲突
    • 即我们在push/pull/merge操作时,最好重新commit提交当前的分支,让其当前分支为最新的提交
image-20220301102511917

8、克隆项目

9、SSH免密登录

  • 找到在上述6.8章生成的ssh公钥
    • 添加ssh公钥到github/gitee

八、命令大全

常用命令

添加、提交、推送
//直接克隆代码
git clone 'https://gitee.com'
//初始化仓库
git init
//添加所有文件到暂存区
git add .  
//提交到仓库区
git commit -m '添加的消息'  
//初始添加远端仓库地址
git remote add origin 'https://gitee.com'
//将本地的master分支推送到远端主机master,同时指定origin为默认主机,以后直接使用git push
git push -u origin master 或者 git push --set-upstream origin master
//推送当前分支到已设置的远端地址
git push
//提交到远端的指定分支
git push origin branchName
//推送到远端地址(默认推送当前分支)
git push 'https://gitee.com' 
//推送到远端地址的master分支
git push 'https://gitee.com' master
//产生一个内容为空的test.txt文件
touch test.txt 
分支操作
//创建分支
git branch branchName
//切换分支
git checkout branchName
//创建并切换分支
git checkout -b branchName
//查看所有分支 有 * 的为当前分支
git branch  
//将当前分支合并到指定分支(合并后的分支,对合并之前的当前分支和指定分支都有影响)
git merge branchName
//拉取代码 相当于git pull 和 git fetch
git pull
简单创建合并分支流程

分支b1:

#1、从master主分拉取最新代码
git pull
#2、创建分支
git branch b1
#3、切换分支
git checkout b1
#4、新增文件1.txt
touch 1.txt
#5、添加和提交到暂存区
git add 1.txt
git commit -m '新增1.txt文件'
#6、首次新建的分支push到远端需要指定远端分支 即再远端新建的分支也为b1
git push origin b1

主分支master:

#1、首先拉取最新的代码
git pull
#2、合并指定的分支
git merge origin/b1
#3、此时合并的下来的文件 已经存在暂存区。若要提交直接提交即可,无需add和commit
git push

其他命令

新建代码库
# 在当前目录新建一个Git代码库$ git init 
# 新建一个目录,将其初始化为Git代码库$ git init [project-name] 
# 下载一个项目和它的整个代码历史$ git clone [url]
配置
# 显示当前的Git配置$ git config --list
# 编辑Git配置文件$ git config -e [--global]
# 设置提交代码时的用户信息$ git config [--global] user.name "[name]"$ git config [--global] user.email "[email address]"
增加/删除文件
# 添加指定文件到暂存区$ git add [file1] [file2] ... 
# 添加指定目录到暂存区,包括子目录$ git add [dir] 
# 添加当前目录的所有文件到暂存区$ git add . 
# 添加每个变化前,都会要求确认
# 对于同一个文件的多处变化,可以实现分次提交$ git add -p 
# 删除工作区文件,并且将这次删除放入暂存区$ git rm [file1] [file2] ...
# 停止追踪指定文件,但该文件会保留在工作区$ git rm --cached [file] 
# 改名文件,并且将这个改名放入暂存区$ git mv [file-original] [file-renamed]
代码提交
# 提交暂存区到仓库区$ git commit -m [message] 
# 提交暂存区的指定文件到仓库区$ git commit [file1] [file2] ... -m [message] 
# 提交工作区自上次commit之后的变化,直接到仓库区$ git commit -a 
# 提交时显示所有diff信息$ git commit -v 
# 使用一次新的commit,替代上一次提交
# 如果代码没有任何新变化,则用来改写上一次commit的提交信息$ git commit --amend -m [message] 
# 重做上一次commit,并包括指定文件的新变化$ git commit --amend [file1] [file2] ...
分支
# 列出所有本地分支$ git branch 
# 列出所有远程分支$ git branch -r 
# 列出所有本地分支和远程分支$ git branch -a 
# 新建一个分支,但依然停留在当前分支$ git branch [branch-name] 
# 新建一个分支,并切换到该分支$ git checkout -b [branch] 
# 新建一个分支,指向指定commit$ git branch [branch] [commit] 
# 新建一个分支,与指定的远程分支建立追踪关系$ git branch --track [branch] [remote-branch] 
# 切换到指定分支,并更新工作区$ git checkout [branch-name] 
# 切换到上一个分支$ git checkout - 
# 建立追踪关系,在现有分支与指定的远程分支之间$ git branch --set-upstream [branch] [remote-branch] 
# 合并指定分支到当前分支$ git merge [branch] 
# 选择一个commit,合并进当前分支$ git cherry-pick [commit] 
# 删除分支$ git branch -d [branch-name] 
# 删除远程分支$ git push origin --delete [branch-name]$ git branch -dr [remote/branch]
标签
# 列出所有tag$ git tag 
# 新建一个tag在当前commit$ git tag [tag] 
# 新建一个tag在指定commit$ git tag [tag] [commit] 
# 删除本地tag$ git tag -d [tag] 
# 删除远程tag$ git push origin :refs/tags/[tagName] 
# 查看tag信息$ git show [tag] 
# 提交指定tag$ git push [remote] [tag] 
# 提交所有tag$ git push [remote] --tags
# 新建一个分支,指向某个tag$ git checkout -b [branch] [tag]
查看信息
# 显示有变更的文件$ git status 
# 显示当前分支的版本历史$ git log
# 显示commit历史,以及每次commit发生变更的文件$ git log --stat 
# 搜索提交历史,根据关键词$ git log -S [keyword] 
# 显示某个commit之后的所有变动,每个commit占据一行$ git log [tag] HEAD --pretty=format:%s 
# 显示某个commit之后的所有变动,其"提交说明"必须符合搜索条件$ git log [tag] HEAD --grep feature 
# 显示某个文件的版本历史,包括文件改名$ git log --follow [file]$ git whatchanged [file] 
# 显示指定文件相关的每一次diff$ git log -p [file] 
# 显示过去5次提交$ git log -5 --pretty --oneline 
# 显示所有提交过的用户,按提交次数排序$ git shortlog -sn 
# 显示指定文件是什么人在什么时间修改过$ git blame [file] 
# 显示暂存区和工作区的差异$ git diff 
# 显示暂存区和上一个commit的差异$ git diff --cached [file] 
# 显示工作区与当前分支最新commit之间的差异$ git diff HEAD 
# 显示两次提交之间的差异$ git diff [first-branch]...[second-branch] 
# 显示今天你写了多少行代码$ git diff --shortstat "@{0 day ago}" 
# 显示某次提交的元数据和内容变化$ git show [commit]
# 显示某次提交发生变化的文件$ git show --name-only [commit] 
# 显示某次提交时,某个文件的内容$ git show [commit]:[filename] 
# 显示当前分支的最近几次提交$ git reflog
远程同步
# 下载远程仓库的所有变动$ git fetch [remote] 
# 显示所有远程仓库$ git remote -v 
# 显示某个远程仓库的信息$ git remote show [remote] 
# 增加一个新的远程仓库,并命名$ git remote add [shortname] [url] 
# 取回远程仓库的变化,并与本地分支合并$ git pull [remote] [branch] 
# 上传本地指定分支到远程仓库$ git push [remote] [branch] 
# 强行推送当前分支到远程仓库,即使有冲突$ git push [remote] --force
# 推送所有分支到远程仓库$ git push [remote] --all
撤销
# 恢复暂存区的指定文件到工作区$ git checkout [file] 
# 恢复某个commit的指定文件到暂存区和工作区$ git checkout [commit] [file] 
# 恢复暂存区的所有文件到工作区$ git checkout . 
# 重置暂存区的指定文件,与上一次commit保持一致,但工作区不变$ git reset [file] 
# 重置暂存区与工作区,与上一次commit保持一致$ git reset --hard 
# 重置当前分支的指针为指定commit,同时重置暂存区,但工作区不变$ git reset [commit] 
# 重置当前分支的HEAD为指定commit,同时重置暂存区和工作区,与指定commit一致$ git reset --hard [commit] 
# 重置当前HEAD为指定commit,但保持暂存区和工作区不变$ git reset --keep [commit] 
# 新建一个commit,用来撤销指定commit
# 后者的所有变化都将被前者抵消,并且应用到当前分支$ git revert [commit] 
# 暂时将未提交的变化移除,稍后再移入$ git stash$ git stash pop