Git使用入门

这篇笔记是我看尚硅谷的教程边看教程边总结下来的,有些地方写的很浅,应该之后会慢慢补充

git同SVN一样,也是一个版本控制软件,实现了版本控制软件具有的「协同修改」、「数据备份」、「权限控制」、「历史记录」、「分支管理」等功能

git最大的优势是拥有SVN没有的「对团队外开发者贡献的代码进行合并审核」的功能,这使得不仅是团队成员而是所有人都可以参与到项目中,这也是GitHub等网站现在如此流行的原因

Git 结构

git分为本地库和远程库,其中远程库在代码托管中心上,可以是GitHub、码云,或者我们自己建的GitLab私服

之前工作也只是在一个master分支下改动,没有用到git最强大的分支管理功能,每次也只是「提交并推送」,可以说和SVN没有区别

这个流程是个轮廓,不保证细节全部准确

Git 本地库

git 在本地库有3个区域,分别是:工作区(working tree)、暂存区(index)、本地仓库

graph LR 工作区 -- 1: git add --> 暂存区 暂存区 -- 2: git commit --> 本地仓库

Git 远程库

如果这个库只是团队内成员操作:

graph TD 组长 -- 1 git push 将初始版本提交远程库 --> 代码托管中心 代码托管中心 -- 2 git clone 克隆初始化本地仓库 --> 程序员1 程序员1 -- 3 git push 提交修改的代码 --> 代码托管中心 代码托管中心 -- 4 git pull 拉取程序员的改动 --> 组长

如果是跨团队协作就稍微复杂一些:

graph TD 组长 -- 1 git push 将初始版本提交远程库 --> 代码托管中心 代码托管中心 -- 2 GitHub fork --> 开发者的托管中心 开发者的托管中心 -- 3 git clone 克隆初始化本地仓库 --> 开发者 开发者 -- 4 git push 提交自己的修改 --> 开发者的托管中心 开发者的托管中心 -- 5 GitHub pull request 合并请求 --> 代码托管中心 代码托管中心 -- 6 组长审核请求并merge --> 组长

Git 基本操作

config 设置签名

要使用git首先需要在config中设置签名,签名中的邮箱可以是不存在的,这个只是为了分辨哪个人提交的

config有2个级别,仓库级/全局

  • 仓库级,仓库级的优先级比全局高,保存在当前 .git 目录下的 config 文件
$ git config user.name "n031"
$ git config user.email "n031@gmail.com"
  • 全局,在家目录(Linux 上是"~")下的 .gitconfig 文件
$ git config --global user.name "n031"
$ git config --global user.email "n031@gmail.com"

add commit 本地库基本操作

$ git init

初始化成功后在目录下会生成一个 .git 隐藏文件

查看当前仓库状态

$ git status
On branch master 
# 在maste 分支
No commits yet 
# 还没有提交记录[本地库]
nothing to commit (create/copy files and use "git add" to track) 
# 也没有什么可提交的(创建/复制文件 并且使用 git add 让 git 去追踪[管理]这个文件)[暂存区,放入暂存区就是被git所追踪]

创建一个good.txt文件,然后再查看状态

$ git status
On branch master
No commits yet
Untracked files:
# 未被追踪文件
  (use "git add <file>..." to include in what will be committed)
        good.txt

nothing added to commit but untracked files present (use "git add" to track)

现在暂存区是空的,将good.txt添加进去

$ git add good.txt
warning: LF will be replaced by CRLF in good.txt. 
# 自动替换成CRLF,如果从windows down下来还是LF
The file will have its original line endings in your working directory
$ git status
On branch master
No commits yet
Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   good.txt

可以将文件从暂存区移除

git rm --cached good.txt

或者提交到本地仓库

# git commit -m "本次注释" <可选文件名>
$ git commit -m "add good.txt" good.txt
[master (root-commit) c0adbbe] add good.txt 
# root-commit根提交,只有一次
 1 file changed, 1 insertion(+) 
 # 1个文件被修改,增加了1行
 create mode 100644 good.txt
 # 仓库创建了ood.txt文件
$ git status
On branch master
nothing to commit, working tree clean

修改good.txt状态后再查看状态

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  # 将修改添加到暂存区
  (use "git restore <file>..." to discard changes in working directory) 
  # 撤销工作区中的修改
  # 之前的版本是git checkout ?
        modified:   good.txt

no changes added to commit (use "git add" and/or "git commit -a")

git add good.txt后查看状态,ps:这里假如把good.txt删除了,然后git rm good.txt也可以把删除这个操作提交到暂存区

$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
  # 撤销添加到暂存区的修改
  # 之前的版本是reset HEAD,删除暂存区全部修改
        modified:   good.txt

这之中用到的操作

$ git add good.txt # 添加good.txt的修改,工作区->暂存区
$ git rm good.txt # 仅删除good.txt时,添加good.txt的修改,工作区->暂存区
$ git restore good.txt # 如果工作区和暂存区不同,工作区恢复成暂存区的版本
$ git restore --staged good.txt # 如果工作区和暂存区不同,暂存区恢复成工作区的版本

大致猜测暂存区起到一个标准的作用,与工作区对比的作用,让工作区不直接和版本库比较,一些“尝试的改动”比较时就更直观容易也更容易恢复

log 查看版本记录

最基础的:git logB查看上一页,Space查看下一页,Q退出

比较简洁且常用的:git log --pretty=onelinegit log --oneline,两种结果相同,

log只显示HEAD之后的版本,不显示之前的版本(后面会提到)

git reflog也可以查看版本日志,比git log多距离当前HEAD几跳的信息

reset 版本前进后退

  • 基于索引值版本重置
$ git reflog
126320d (HEAD -> master) HEAD@{0}: reset: moving to 126320d
f9752a6 HEAD@{1}: reset: moving to @
f9752a6 HEAD@{2}: reset: moving to f9752a6
126320d (HEAD -> master) HEAD@{3}: reset: moving to 126320d
f9752a6 HEAD@{4}: reset: moving to f9752a6
126320d (HEAD -> master) HEAD@{5}: commit: add bad
f9752a6 HEAD@{6}: commit: modify good.txt
9fc37f5 HEAD@{7}: commit (initial): add good.txt
# 注意这里面的一些版本号是相同的,因为只是通过reset操作前进恢复了一些版本,并没有生成新的文件

使用索引恢复实质上就是移动HEAD指针位置,执行命令

$ git reset --hard 9fc37f5

HEAD指针就会移动到"9fc37f5"这个版本

  • 使用^~后退版本

^只能后退版本,不能向前选择版本,通常使用git reset --hard HEAD^回退到HEAD之前的一个版本,有几个^就表示后退几个版本

~也只能后退版本,~n就表示后退n步,在^比较多的时候使用,例如git reset --hard HEAD^^^^^git reset --hard HEAD~5是等价的

hard/soft/mixed 重置版本的区别

  • soft

只是在本地库移动下指针,但不会碰工作区和暂存区

将本地库恢复凸显暂存区的变动了

  • mixed(默认)

本地库移动下指针,重新设置暂存区,但不重置工作区,所以现在的文件内容会保存

将本地库和暂存区恢复,就凸显工作区的变化了,reset HEAD可以用来清除暂存区

  • hard

本地库移动下指针,重置暂存区,重置工作区,所有之后修改的代码都会被丢弃

正常情况,版本可以使用--hard恢复,但暂存区和工作区文件就恢复不了了

soft mixed hard
工作区 working tree 恢复
暂存区 index 恢复 恢复
本地库 恢复 恢复 恢复

diff 比较文件

git diff,无参数,表示工作区与暂存区比较

git diff <file>表示指定哪个文件比较

git diff可以加分支参数,如git diff HEAD表示工作区和当前本地库最新版本比较,git diff HEAD^表示工作区和本地库上一个版本比较

这里比较下bad.txt文件,7/9/10行表示具体内容,git认为的修改就是删除又新增,因为1后面加了个回车,所以是删除1行,新增2行

$ git diff bad.txt
diff --git a/bad.txt b/bad.txt
index 56a6051..7a754f4 100644
--- a/bad.txt
+++ b/bad.txt
@@ -1 +1,2 @@
-1
 No newline at end of file
+1
+2
 No newline at end of file

Git 分支操作

branch 创建查看分支

  • 创建分支
$ git branch [分支名]
  • 查看分支
$ git branch -v

checkout 切换分支

$ git checkout [分支名]

merge 合并分支

  1. 切换到被合并的分支上
  2. 执行merge命令,例如刚刚创建了一个bak分支,那么就是git merge bak,具体是
    1. git checkout master切换到master分支
    2. git merge bak合并aaa分支的内容

分支合并冲突

  • 例如创建了一个bak分支,然后bak分支和master分支各修改了相同的一行,然后将master合并到bak,就会出现问题
Administrator@IGAME MINGW64 /d/GitHub/guigu (bak)
# 当前是bak分支
$ git merge master
Auto-merging good.txt
CONFLICT (content): Merge conflict in good.txt
Automatic merge failed; fix conflicts and then commit the result.
# 自动合并失败,修复冲突,然后手动commit
Administrator@IGAME MINGW64 /d/GitHub/guigu (bak|MERGING)
# 当前bak分支,但变成MERGING状态

good.txt内容就变成了

aaa
bbb
<<<<<<< HEAD # 冲突开始
ccc edit by bak
======= # 上面是当前分支的内容,下面是合并来的分支的内容
ccc edit by master
>>>>>>> master # 冲突结束
ddd
eee edit by bak # 这句话是bak合并的,并未产生冲突解决
  • 解决方法就是手动修改good.txt文件,保存

先用git status查看下当前状态

$ git status
On branch bak
You have unmerged paths.
# 你有一个未合并的路径
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
# 未合并路径
  (use "git add <file>..." to mark resolution)
  # 使用"git add <file>..."去标记已解决
        both modified:   good.txt

no changes added to commit (use "git add" and/or "git commit -a")
  • git add good.txt,再查看状态
$ git status
On branch bak
All conflicts fixed but you are still merging.
# 所有冲突都解决得了,但你仍然处于merging状态
  (use "git commit" to conclude merge)
  # 使用"git commit"解决merging

Changes to be committed:
        modified:   good.txt
  • 然后只需要git commit即可提交,这次不用加文件名

GitHub

remote 设置别名

在GitHub中,仓库的地址是一长串很麻烦,不可能每次都输入这一长串东西,所以git提供了一个别名的功能

查看所有git的别名:git remote -v

添加别名

$ git remote add mf https://github.com/lk722722722/MyFirstRepo.git

添加后会发现有2条记录

$ git remote -v
mf      https://github.com/lk722722722/MyFirstRepo.git (fetch)
mf      https://github.com/lk722722722/MyFirstRepo.git (push)

由于这样设置别名是仓库级的,所以一般都使用origin表示远端位置

$ git remote rm mf # 删除之前添加的mf别名
$ git remote add origin https://github.com/lk722722722/MyFirstRepo.git # 设置新别名

push 向远端推送 - 需要凭证

由于已经设置好了别名,将本地库文件推送到GitHub只需

$ git push -u origin master

如果创建GitHub仓库的时创建了README.md文件,那本地库推送之前也需要创建此文件,否则将会提示推送失败

$ git push origin master
To https://github.com/lk722722722/MyFirstRepo.git
 ! [rejected]        master -> master (fetch first)
error: failed to push some refs to 'https://github.com/lk722722722/MyFirstRepo.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

解决方法是先执行一遍合并操作

$ git pull --rebase origin master

clone 克隆

很简单,只需要这样即可,在克隆之前也无需init初始化仓库

$ git clone https://github.com/lk722722722/MyFirstRepo.git

克隆完,不仅把远端的文件下载到本地,还init了本地.git目录,并且创建了origin别名

远程库的拉取

pull = fetch + merge

fetch 远程库拉取

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

merge 远程库合并

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

注意:merge拉取的时候实际上远端的文件已经下载到了本地

远端合并思想和本地库合并相同,只是分支名前多了远程库地址/表示和本地不同的另外一个分支

pull 远程库拉取合并

实际上是先fetchmerge

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

两种拉取的选择

fetch之后文件已经下载到本地,此时已经可以查看文件,只不过没有合并到我们本地工作的仓库中,so:

  • 第一次拉取用clone
  • 之后拉取,文件比较复杂,用fetch+merge
  • 之后拉取,文件较清晰,用pull比较省事

团队合作中的冲突

团队合作中肯定会有冲突,当两个人同时修改一个文件, 只有先推送(push)的那个人可以推送到远程库里面,后推送的人只能先拉取(pull)再推送(push)。拉取的时候,修改的位置有自己的内容,也有另一个的内容,这就必须得我们自己去处理冲突(MERGING状态)

  1. 两人分别修改,提交到各自的本地库都没问题(初始化版本A0,HEAD版本A0)
  2. A先执行,git push origin master,没有问题,A把他的版本提交上去了(版本号A,基于parent A0版)
  3. B再执行,git push origin master,就出问题了,此时B的版本不是最新版本(版本号B,基于parent A0版,不是最新HEAD版A)
  4. B需要先 git pull origin master将远端的版本下载下来,此时本地库是MERGING状态
  5. B修改代码,addcommit。现在本地库已经是基于A的版本号新提交的一个版本,再执行git push origin master没有问题(版本号B2,基于parent A/B版)
  6. 如果A还要修改,A也也要pulladdcommitpush,否则因为A的当前版本不是基于最新版本修改的(A本地生成的A2,parent版本是A,不是基于HEAD的版本B2)

图例,总之push的时候要确保本地的版本是基于远端的HEAD版本做的修改,push才能被接受

graph BT A0 -- 1 初始版本 --> A A0 -- 2 B的修改晚了一步 --> B A -- 3 B合并HEAD A的内容 --> B2 B -- 3 B也合并自己的内容 --> B2 A -- 4 A又提交了一个版本 --> A2 B2 -. 4 A还需合并HEAD B2的内容 .-> A2 A2 -. 5 以后都是基于HEAD版本编辑 .-> A3

SSH免密登录

  1. 生成秘钥
$ ssh-keygen -t rsa -C xxxxxxx@qq.com
  1. 之后会生成一个/.ssh目录,下面有id_rsaid_rsa.pub两个文件
  2. id_rsa.pub复制到GitHub的SSH keys处,这样就设置好了,以后就不用输入账号密码了
  3. 这样提交是以ssh协议提交,所以远程仓库的地址变了
$ git push origin_ssh master

IDE

git ignore

IDE会产生很多和IDE版本相关的文件,如果这些文件添加到GIt管理,在团队中每次处理这些文件都将是一件麻烦且毫无意义的是事情,所以需要将这些文件添加到忽略列表中,具体有哪些文件,GitHub列出了一个详细的清单

<!-- 项目地址 -->
https://github.com/github/gitignore
<!-- 与Java相关 -->
https://github.com/github/gitignore/tree/master/Global
https://github.com/github/gitignore/blob/master/Global/Eclipse.gitignore
https://github.com/github/gitignore/blob/master/Global/JetBrains.gitignore

配置方法:

在仓库目录下新建一个名为.gitignore的文件(因为是点开头,没有文件名,没办法直接在windows目录下直接创建,必须通过右键Git Bash,按照linux的方式来新建.gitignore文件)。 通过将.gitignore文件添加到仓库,其他开发者更新该文件到本地仓库,以共享同一套忽略规则

在IDEA使用Git(待添加)

这部分尚硅谷的视频中没有,待之后我添加

Git 工作流(待补充)

GitLab(待添加)

待添加

原文地址:https://www.cnblogs.com/n031/p/11870152.html