Git 笔记

发布于 2024-05-08
1. 基础概念 工作区 Working Directory: 就是你在文件管理器中看到的目录,包含你的文件,.git 目录,里面包含所有内容,如修改但未跟踪(未添加到暂存区)的文件,git add 暂存的文件、git commit 提交后的版本库、当前所处分支等信息。 版本库 Repository: 工作区下有个…

1. 基础概念

  • 工作区 Working Directory: 就是你在文件管理器中看到的目录,包含你的文件,.git 目录,里面包含所有内容,如修改但未跟踪(未添加到暂存区)的文件,git add 暂存的文件、git commit 提交后的版本库、当前所处分支等信息。
  • 版本库 Repository: 工作区下有个 .git 目录,里面存储了 git 运行所需的所有数据,包括暂存区 stage、分支信息、提交信息等。
  • 暂存区 Stage:

2. 暂存区 stage

2.1. 添加到暂存区 stage

# 添加所有文件
git add .

# 添加指定文件
git add file.txt

# 添加指定目录
git add dir/

# 使用通配符
git add *.txt

2.2. 从暂存区移除

# 移除所有文件, 其中 HEAD 表示最新的提交
git reset HEAD

# 移除指定文件
git reset HEAD file.txt

# 新语法
git restore --staged file.txt

这样操作不会恢复你修改的内容,只是从暂存区移除而已。

3. 提交到本地仓库 commit

# 指定消息提交
git commit -m "message"

# 添加所有文件到暂存区并提交 (等于自动先执行了 git add .)
git commit -am "message"

4. 查看提交记录 log

# 查看提交记录
git log

# 以每条记录一行的简洁方式查看
git log --pretty=oneline

# 包含分支的图表情况
git log --graph

显示的结果是当前提交以及之前的提交,时间倒序排列。

5. 回退到上个版本 reset

# 恢复到上个版本
git reset --hard HEAD^

# 恢复到上上个版本
git reset --hard HEAD^^

# 恢复到上 n 个版本
git reset --hard HEAD~n

# 恢复到指定 commit_id 的版本, commit_id 可以通过 git log 查看
git reset --hard commit_id

6. 查看操作记录 reflog

如你提交了 5 个版本,恢复到了第 2 次提交后,你想再恢复到第 5 次,但通过 git log 只能看到第 1-2 次提交的记录,第 3-5 次提交的记录看不到了,这时可以通过 git reflog 查看所有操作记录。

git reflog

7. 撤销修改 checkout

# 撤销工作区的修改
git checkout -- file

# 新语法
git restore file.txt

注意上面的命令是撤销工作区的修改,具体分为以下几种情况:

  • 文件修改自修改后还未提交到暂存区:撤销后文件会和版本库的内容一致。
  • 文件提交到过暂存区,但再次修改后未提交到暂存区:撤销后文件会和暂存区的内容一致。

如果已经提交到了暂存区,可以参考上面的 从暂存区移除.

其中 -- 是为了避免歧义,表示后面的是文件名,不是分支名。

8. 比较差异

# 工作区与暂存区比较
git diff file 

# 暂存区与版本库比较, 也可以用 --staged, 效果是一样的
git diff --cached file

# 工作区与版本库比较
git diff HEAD file

# 工作区和某次提交比较
git diff commit_id file

# 比较两次提交
git diff commit_id1 commit_id2

9. 分支管理 branch

9.1. 创建, 查看, 切换, 合并, 删除分支

9.1.1. 添加分支

# 创建分支
git branch branch_name

# 创建并切换到该分支
git checkout -b branch_name
git switch -c branch_name

9.1.2. 查看分支

# 查看所有分支
git branch

9.1.3. 切换分支

git checkout branch_name
git switch branch_name

9.1.4. 合并分支 merge

# 合并指定分支到当前分支
git merge branch_name

# 合并分支时,加上 --no-ff 参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而默认的 fast forward 合并就看不出来曾经做过合并。
# 如 master 合并 dev 分支的修改,从指针上说:
#        fast forward 模式: 直接将 master 指针指向 dev 指针指向的内容.
#     no fast forward 模式: master 新建了一个跟 dev 指针内容一样的空间,并将 master 指向这个新 new 的指针. 
git merge --no-ff -m "merge branch xxx" branch_name

9.1.5. 删除分支

git branch -d branch_name

# 如果分支未合并,则上面的命令会删除失败,需使用 -D 参数强制删除
git branch -D branch_name

9.1.6. 重命名分支

# 修改当前所在分支名
git branch -m new_branch_name

# 修改指定分支名
git branch -m old_branch_name new_branch_name

9.2. 冲突解决

当合并分支时,如果有冲突,会提示:

Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the result.

执行 git status 会显示冲突的文件:

On branch main
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)
	both modified:   file.txt

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

提示我们有两个选择,手动解决冲突或放弃合并。

放弃冲突将会恢复合并前的状态:

git merge --abort

手动合并的话,需要编辑冲突的文件,先来看看冲突文件的内容:

hello world java !
nihao
hello python;
add from main branch
<<<<<<< HEAD
2
=======
1
>>>>>>> dev

上面的内容表示冲突的部分,<<<<<<< HEAD======= 之间是当前分支的内容,=======>>>>>>> dev 之间是合并的分支的内容,我们需要手动解决冲突,编辑后再提交。

10. 高级技巧

10.1. stash 暂存区

如你正在开发一个功能,但突然有个 bug 需要修复,但是你本地还有开发一半的功能没法提交,这时可以使用 git stash 命令将当前工作区的内容暂存起来,等 bug 修复后再恢复。

# 暂存当前工作区
git stash

修复 bug 后,回到开发分支后可以通过 git stash list 查看暂存的内容:

stash@{0}: WIP on main: 1389c31 merge bug branch

stash@{0} 表示最新一次暂存的内容,如果多次暂存会以压栈的方式存储,后面的内容是暂存时的分支信息:WIP 表示工作区(Working Directory)的意思,main 表示当前分支,1389c31 表示提交的 commit_id,merge bug branch 表示提交的消息。

可以通过 git stash apply 恢复暂存的内容(恢复栈顶也就是最新一次暂存的内容),不过恢复完后没有从栈中移除,可以通过 git stash drop 删除栈顶的内容,或者恢复时使用 git stash pop 恢复并删除栈顶的内容。

如果你不是要恢复栈顶的内容,可以通过 git stash list 查看暂存列表信息,然后通过 git stash apply stash@{n} 恢复指定的内容。

命令参考:

命令说明
git stash list查看暂存列表
git stash apply stash@{n}恢复暂存内容
git stash drop stash@{n}删除暂存内容
git stash pop stash@{n}恢复并删除暂存内容
git stash show stash@{n}查看暂存的内容(修改的文件)
git stash branch branch_name stash@{n}从暂存创建新分支
git stash save "message"暂存并添加消息
git stash create创建一个暂存,但不应用
git stash clear清空暂存列表

10.2. cherry-pick 优选

如你有两个分支 master, dev, 你正在 dev 分支开发,按照上一节的 stash 功能你为了修复紧急问题创建了个 bug 分支,修复后合并到 master 分支。

但是你正在开发的 dev 分支也需要这个修复,这时可以使用 cherry-pick 命令将 master 分支的修复 "复制"dev 分支。

如:

# 在 dev 上执行, commit_id 是 master 分支的修复提交
git cherry-pick commit_id

这样会自动将指定的 commit 复制到当前分支并提交(如果有冲突需要手动解决),提交的日期、提交人、提交消息也是和优选的提交一样。

11. 多人协作 - 远程仓库

命令说明
git remote查看远程仓库名称
git remote -v查看远程仓库详细信息
git remote add <fetch|push> origin repo_url添加远程仓库, fetch 是拉取, push 是推送
git remote rm origin删除远程仓库
git remote show origin查看远程仓库详细信息
git clone repo_url克隆远程仓库
git push origin master把当前所在本地分支推送到远程仓库 origin 的 master 分支
git push -u origin branch_name一次性推送并关联远程分支
git pull origin master拉取远程仓库 origin 的 master 分支
git branch --set-upstream-to=origin/branch_name branch_name关联远程分支
git branch -vv查看本地分支关联的远程分支

关联远程仓库分支

git clone 时默认只会克隆远程仓库的主分支,如果想克隆其他分支,可以通过 git checkout -b branch_name origin/branch_name 创建并切换到指定分支,后面的 origin/branch_name 是关联的远程分支,如果不指定就是基于当前本地的分支创建新分支,而不是关联获取远程分支了。

如果你在本地创建了一个新分支,想推送到远程仓库,可以通过 git push origin branch_name 推送到远程仓库,如果远程仓库没有这个分支,会自动创建。

不过这样你每次推送都需要指定 origin branch_name,可以通过 git push -u origin branch_name 一次性推送并关联远程分支,以后推送就可以直接 git push 了。

如果你忘记加 -u 参数,可以通过 git branch --set-upstream-to=origin/branch_name branch_name 关联远程分支。

你可以通过 git branch -vv 查看本地分支关联的远程分支。