git-代码版本管理工具


git 下载地址open in new window

git 学习网址open in new window


1.git 常用命令

1.1 git init:初始化仓库

mkdir project
cd project

git init

指定目录作为 git 仓库

git init project

1.2 git clone:克隆仓库

git clone [url]

1.3 git add:添加

将一个文件添加至 stage(暂存区)

# 添加某一个文件
git add [file]

# 全部添加
git add .

1.4 git status:查看状态

git status

1.5 git diff:显示变更

# 显示尚未添加到stage的文件的变更。
git diff

# 显示添加到stage的文件与当前最新版本之间的差异
git diff –staged

# 显示两个分支之间的差异
git diff [first branch] [second branch]

1.6 git commit:提交

从工作区提交到暂存区

git commit "[Type in the commit message]"

1.7 git reset:重置

情景解析:

–mixed(默认):重置位置的同时,只保留 Working Tree 工作目录的內容,但会将 Index 暂存区 和 Repository 中的內容更改和 reset 目标节点一致,因此原节点和 Reset 节点之间的【差异变更集】会放入 Working Tree 工作目录中。所以效果看起来就是原节点和 Reset 节点之间的所有差异都会放到工作目录中。

–hard:重置位置的同时,直接将 working Tree 工作目录、 index 暂存区及 repository 都重置成目标 Reset 节点的內容,所以效果看起来等同于清空暂存区和工作区。

–soft:重置位置的同时,保留 working Tree 工作目录和 index 暂存区的内容,只让 repository 中的内容和 reset 目标节点保持一致,因此原节点和 reset 节点之间的【差异变更集】会放入 index 暂存区中(Staged files)。所以效果看起来就是工作目录的内容不变,暂存区原有的内容也不变,只是原节点和 Reset 节点之间的所有差异都会放到暂存区中。

应用场景:

–hard:(1)放弃本地的所有改变,即去掉所有 add 到暂存区的文件和工作区的文件;(2) 抛弃目标节点后的所有 commit(之后所有的 commit 都不对)。

–soft:合并差异不大的 commit,使整个演进线图更清晰。

–mixed(默认):(1)与 2 类似,合并相似的 commit;(2)移除暂存区中的内容。(3)commit 提交某些错误代码,不想再修改错误再 commit(因为会留下一个错误 commit 点),可以回退到正确的 commit 点上,然后所有原节点和 reset 节点之间差异会返回工作目录,假如有个没必要的文件的话就可以直接删除了,再 commit 上去就 OK 了。

案例:

# 原节点和 Reset 节点之间的所有差异都会放到工作目录中(不会更改工作区的代码)
git reset

# 丢弃所有的历史记录,并回滚到指定的提交(会更改工作区的代码)
git reset --hard

# 主分支丢弃所有的历史记录,并回滚到指定的提交(会更改工作区)
git reset --hard origin/master

#  可以 拉取最近一次提交到版本库的文件到暂存区 并且该操作不影响工作区
git reset HEAD --

1.8 git rm:删除

删除工作目录中的文件,并将删除动作添加到 stage。

git rm [file]

1.9 git mv:移动或重命名

移动或重命名一个文件、目录、软连接

git mv oldname newname

1.10 git log:历史记录

用于显示当前分支的版本历史记录

git log

显示某个文件的版本历史记录,包括文件的重命名

git log –follow [file]

1.11 git push:发送到远程代码库

将主分支上提交的变更发送到远程代码库

git push [variable name] master

将指定分支上的提交发送到远程代码库。

git push [variable name] [branch]

将所有分支发送到远程代码库。

git push –all [variable name]

git push --all origin
# 上面命令表示,将所有本地分支都推送到origin主机。

--force :

使用–force 选项,结果导致在远程主机产生一个”非直进式”的合并(non-fast-forward merge)。

除非你很确定要这样做,否则应该尽量避免使用–force 选项

git push --force origin

删除远程分支

git push –d [branch]

git push -d origin v1.0
# 删除分支v1.0

-u 参数

git push -u origin master

git push -u origin master 执行添加了-u 参数的命令,就相当于是执行了

git push origin master 和 git branch --set-upstream master origin/master。

所以,在进行推送代码到远端分支,且之后希望持续向该远程分支推送,则可以在推送命令中添加 -u 参数,简化之后的推送命令输入。

1.12 git pull:获取远程服务器的变更

将获取远程服务器上的变更,并合并到你的工作目录。

git pull [Repository Link]

1.13 git stash:临时保存

将临时保存所有修改的文件。

git stash save

将恢复最近一次 stash(储藏)的文件。

git stash pop

将显示 stash 的所有变更。

git stash list

将丢弃最近一次 stash 的变更。

git stash drop

1.14 git branch/checkout: 分支管理

列出分支

# 当前分支
git branch

# 所有分支
git branch -av

创建分支

git branch [分支名]

# 创建分支2.0(不切换)
git branch v2.0

删除本地分支

git branch -d v2.0
# 结果:Deleted branch v2.0 (was 3d47fcd).

删除远程分支

git push -d origin v1.0

切换分支

# 切换到分支2.0,如果有远程分支v2.0,本地将新建v2.0分支,并关联远程v2.0分支
git checkout v2.0

# 本地将新建并切换到local-v2.0分支,并关联远程v2.0分支
git checkout -b local-v2.0 origin/v2.0

# 创建新的v2.0分支并切换到分支(v2.0分支必须不存在)
git checkout -b v2.0
# 结果:Switched to a new branch 'v2.0'

合并分支

git merge [分支名]

# 例:当前所在分支master,合并分支v2.0
git merge v2.0

取消合并分支

git merge --abort

1.15 git tag:标签

# 直接列出所有的标签
git tag

# 可以根据 xxxx 进行标签的筛选
git tag -l xxxx :

查看标签和它的备注:

git tag -l -n

# 运行结果
# build1          tags
# build2          tags
# test            add
# test1           tags
# test2           Merge branch 'local-v2.0'
# testTag2        tastTagRemark2

查看线上代码库的标签

git ls-remote --tags

# 运行结果
# a80ed7c46fd68f973179753af76095ddd226a0a6        refs/tags/build2
# 8b9624d7f22a82f64b718d6cfcfba35a0f715fc8        refs/tags/test
# 82292aaf03a624d48df3a4ef0d24214c61a86ae3        refs/tags/testTag2

查看标签的提交信息

git show 标签名

创建轻量标签

#  直接给当前的提交版本创建一个【轻量标签】
git tag 标签名

# 例如:给当前版本一个tag "test"
git tag test

# 给指定的提交版本创建一个 【轻量标签】
git tag 标签名 提交版本

# 例如:给版本"9398f2ad"一个tag "test2"
git tag test2 9398f2ad

创建附注标签

  • -a : 理解为 annotated 的首字符,表示 附注标签
  • -m : 指定附注信息
git tag -a 标签名称 -m 附注信息
# 例 给当前版本一个tag "testTag1" 附注信息 "tastTagRemark"
git tag -a testTag1 -m tastTagRemark


git tag -a 标签名称 提交版本号 -m 附注信息
# 例:给版本"9398f2ad"一个tag "test2" 附注信息 "tastTagRemark2"
git tag -a testTag2 9398f2ad -m "tastTagRemark2"

删除标签

git tag -d 标签名称
# 例 删除tag "testTag1"
git tag -d testTag1
# 结果:Deleted tag 'testTag1' (was d3f0ab8)

推送到远程仓库

# 将指定的标签上传到远程仓库
git push origin 标签名称

# 将指定的标签testTag1上传到远程仓库
git push origin testTag1
# 结果:* [new tag]         testTag1 -> testTag1

# 将所有不在远程仓库中的标签上传到远程仓库
git push origin --tags

# 结果:
#  * [new tag]         build1 -> build1
#  * [new tag]         build2 -> build2
#  * [new tag]         test2 -> test2
#  * [new tag]         testTag2 -> testTag2

删除远程仓库的标签

git push origin --delete 标签名称
git push origin -d       标签名称

# 删除远程标签test2
git push origin --delete test2
# 运行结果: - [deleted]         test2

# 删除远程标签build1
git push origin -d build1
# 运行结果 - [deleted]         build1

检出标签:

以标签指定的版本为基础版本,新建一个分支,继续其他的操作。因此 ,就是 新建分支的操作了

git checkout -b 分支名称 标签名称

2.仓库建立、导入、关联

已有文件夹或仓库

cd existing_folder
git init
git remote add origin https://codeup.aliyun.com/123456/test.git
git add .
git commit
git push -u origin master

导入代码库

git clone --bare https://git.example.com/your/project.git your_path
cd your_path
git remote set-url origin https://codeup.aliyun.com/123456/test.git
git push origin --tag && git push origin --all

代码库关联到新的仓库地址

git remote set-url origin https://codeup.aliyun.com/123456/test.git
git push

3.github ssh 配置

3.1 配置用户名和邮箱

git config --global user.name "masecho"
git config --global user.email "masecho@163.com"

3.2 生成密钥

使用 ssh-keygen 生成密钥

-t 加/解密算法
-b 秘钥长度,rsa 默认秘钥长度的为 2048
-C 注释,一般是填写用户名

执行以下命令:

ssh-keygen -t rsa -C masecho@163.com

之后显示

Generating public/private rsa key pair.
Enter file in which to save the key (/c/Users/Administrator/.ssh/id_rsa):

之后一直回车,在目录文件夹(C:/Users/Administrator/.ssh)中自动生成文件 id_rsa 与 id_rsa.pub。

3.3 复制公钥内容到 github

使用 notepad++打开 id_rsa.pub 文件,复制文件全部文本内容
进入 github 网站,登录后进入个人 setting->SSH and GPG keys,
进入 SSH keys 页面 然后 Add new,粘贴 id_rsa.pub 文件全部文本内容,保存

3.4 测试与 github 的通信

ssh -T git@github.com

多测试几次

3.5 本地使用多个 github 账号

生成第二个 github 的账号的密钥,生成文件名称为 masechozhang_id_rsa,账号为masechozhang@gmail.com


cd ~/.ssh/
ssh-keygen -t rsa -f ~/.ssh/masechozhang_id_rsa -C "masechozhang@gmail.com"

复制公钥内容到 github:setting->SSH and GPG keys(参考 3.3 复制公钥内容到 github)

~/.ssh/中创建 config 文件

vim config

文件内容如下,config

# 配置默认账号对应的ssh key
Host github.com
HostName ssh.github.com
User masecho
IdentityFile ~/.ssh/id_rsa

#配置另一个帐号
Host mz.github.com
HostName ssh.github.com
User masechozhang
IdentityFile ~/.ssh/masechozhang_id_rsa

添加密钥

#默认操作系统是不开启 ssh-agent 的,需要手动打开
ssh-agent bash
ssh-add ~/.ssh/masechozhang_id_rsa

#输出结果:
Identity added: /c/Users/Administrator/.ssh/masechozhang_id_rsa (masechozhang@gmail.com)

3.6 gitignore 不生效的情况

删除 git 本地缓存,再提交

git rm -r --cached .

4. 分支策略

分支策略:

  • master 分支是整个项目的主分支
  • develop 分支反映最新的开发
  • feather 分支用于某个新功能的开发
  • release 分支用于准备新版本的发布。
  • hotfix 分支用于紧急 bug 修复

4.1 master 分支

  • 所有其他分支都直接或间接源自 master。master 分支只用于产品发布,不能在上面进行工作。

4.2 develop 分支

  • develop 中的代码总是可以完整编译的。
  • 当 develop 中的代码进入稳定状态(修复了绝大多数 bug)准备 release 时,所有 develop 中的更改将通过 release branch 最终 merge 到 master。
  • master 和 develop 分支要和远程仓库保持一致

4.3 feather 分支

  • feather 分支用于某个新功能的开发,源自 develop,并最终 merge 到 develop。
  • feather 分支最终要么合并到 develop 分支,要么被删除。

4.4 release 分支

  • release 分支用于准备新版本的发布。源自 develop,merge 到 develop 和 master。
  • release 分支仅修复小的 bug,不能添加新功能。而 develop 分支可以同时开始新功能的开发。该分支上修复的 bug 需要 merge 到 develop,并在该分支完成时 merge 到 master。此时需要给 master 打上 tag,标记这个新的 release。

4.5 hotfix 分支

  • Hotfix 分支用于紧急 bug 修复,源自 master,merge 到 develop 和 master。
  • 对于已经上线的产品,可能有意外的紧急 bug 需要修复。hotfix branch 可以避免修复 bug 的工作影响 develop 分支

5.git 常见问题

5.1 git merge 与 git rebase 的区别

git merge

  • 使用 merge 是很好的方式,因为它是一种非破坏性的操作,对现有分支不会以任何方式被更改。
  • 另一方面,这也意味着 bugFix 分支每次需要合并上游更改时,它都将产生一个额外的合并提交。
  • 如果 main 提交非常活跃,这可能会严重污染你的 bugFix 分支历史记录。不过这个问题可以使用高级选项 git log 来缓解

git merge 示例:

# 创建新分支 bugFix
git branch bugFix

# 用 git checkout bugFix 命令切换到该分支
git checkout bugFix

# 提交一次
git commit -m "bugFix xxxxxx"

# 用 git checkout main 切换回 main
git checkout main

# 再提交一次
git commit -m "main xxxxxx"

# 用 git merge 把 bugFix 合并到 main
git merge bugFix

Alt text

git rebase

  • rebase 会将整个 bugFix 分支移动到 main 分支的顶端,从而有效地整合了所有 main 分支上的提交。

  • 但是,与 merge 提交方式不同,rebase 通过为原始分支中的每个提交创建全新的 commits 来重写项目历史记录,特点是仍然会在 bugFix 分支上形成线性提交

  • rebase 的主要好处是可以获得更清晰的项目历史。首先,它消除了 git merge 所需的不必要的合并提交;其次,正如你在上图中所看到的,rebase 会产生完美线性的项目历史记录,你可以在 bugFix 分支上没有任何分叉的情况下一直追寻到项目的初始提交。

  • 当前 git status 在哪个分支上,rebase 之后的最新分支就在哪个分支上。

git rebase 示例:

# 新建并切换到 bugFix 分支
git checkout -b bugFix

# 提交一次
git commit -m "bugFix xxxxxx"

# 切换回 main 分支再提交一次
git checkout main
git commit -m "main xxxxxx"

# 再次切换到 bugFix 分支,rebase 到 main 上
git checkout bugFix
git rebase main

Alt text

如何选择 git merge 和 git rebase?

  • git merge 优点是分支代码合并后不破坏原分支的代码提交记录,缺点就是会产生额外的提交记录并进行两条分支的合并,
  • git rebase 优点是无须新增提交记录到目标分支,rebase 后可以将对象分支的提交历史续上目标分支上,形成线性提交历史记录,进行 review 的时候更加直观

git rebase 的黄金原则:不能在一个共享的分支上进行 Git rebase 操作。

总结:

融合代码到公共分支的时使用 git merge,而不用 git rebase

融合代码到个人分支的时候使用 git rebase,可以不污染分支的提交记录,形成简洁的线性提交历史记录

5.2 HEAD 指针指向回退

# 回退一个版本
git checkout HEAD^
git checkout HEAD~1

# 回退2个版本
git checkout HEAD^^
git checkout HEAD~2

# 回退3个版本
git checkout HEAD^^^
git checkout HEAD~3

5.3 将分支转移

# 将bugFix的分支指向当前分支的后退的三个版本
git branch -f bugFix HEAD~3

# 将当前的main分支指向c6的提交版本
git branch -f main c6

5.4 回退版本 Git Reset(不保留撤销记录)

git reset 通过把分支记录回退几个提交记录来实现撤销改动。你可以将这想象成“改写历史”。 git reset 向上移动分支,原来指向的提交记录就跟从来没有提交过一样。

git checkout main
# 本地回退一个版本
git reset HEAD~1

Alt text

Git 把 main 分支移回到 C1;现在我们的本地代码库根本就不知道有 C2 这个提交了。 (译者注:在 reset 后, C2 所做的变更还在,但是处于未加入暂存区状态。)

5.5 回退版本 Git revert(增加撤销的记录)

为了撤销更改并分享给别人,我们需要使用 git revert

# 回退当前版本
get revert HEAD

Alt text

  • 在我们要撤销的提交记录后面居然多了一个新提交!这是因为新提交记录 C2' 引入了更改
  • 这些更改刚好是用来撤销 C2 这个提交的。也就是说 C2' 的状态与 C1 是相同的。
  • revert 之后就可以把你的更改推送到远程仓库与别人分享啦。

示例:

revert 相当于在撤回基础上增加一条撤回的记录,如果再次当前撤回,那么相当于

比如 v1.0-change3,我撤回一次 revert HEAD, 增加记录:Revert "v1.0-change3",代码会回到 v1.0-change3 之前的状态(v1.0-change2),再次撤回 revert HEAD,增加记录:revert change3-1 Revert "Revert "v1.0-change3" 相当于回到 v1.0-change3,第二次撤回抵消了第一次撤回,但是前面的记录也会记录下来


commit fbad6c9623e69b7df180fcec6636751365862266 (HEAD -> v1.0)
Author: masecho
Date:   Tue Jun 27 17:15:42 2023 +0800

    revert change3-1

    Revert "Revert "v1.0-change3""

    This reverts commit f6cbf3a049c9b32cbd552e0b8b9e88879654f110.



commit f6cbf3a049c9b32cbd552e0b8b9e88879654f110
Author: masecho
Date:   Tue Jun 27 17:14:46 2023 +0800

    Revert "v1.0-change3"

    This reverts commit 47122ca9981614e6e1d42fd2aed7d72d48f320db.



commit 47122ca9981614e6e1d42fd2aed7d72d48f320db
Author: masecho
Date:   Tue Jun 27 17:14:13 2023 +0800

    v1.0-change3



commit 01ec80d84056799e6ed41a6d0b9913e1cb6fde6e
Author: masecho
Date:   Tue Jun 27 17:13:44 2023 +0800

    v1.0-change2

5.6 复制分支 git cherry-pick

git cherry-pick <提交号>
  • 如果你想将一些提交复制到当前所在的位置(HEAD)下面的话, Cherry-pick 是最直接的方式了

例子:

我们想将 side 分支上的工作复制到 main 分支,只需要提交记录 C2 和 C4,所以 Git 就将被它们抓过来放到当前分支下了。 就是这么简单

当前:
Alt text

# 执行cherry-pick复制
git cherry-pick c2 c4

结果:
Alt text

5.7 调整提交顺序 get rebase -i

例子:目前我们有五次提交,分别为 rebase-file(1-5)

我们要更改顺序为 1、2、4、5、3

commit c4abcc2dcbb785042ef7fc5df50a36c68cca78e4
Author: masecho
Date:   Wed Jun 28 10:25:41 2023 +0800

    rebase-file5

commit 1609eb7f6b3eb2e90f1c5f564222cad746ad9b02
Author: masecho
Date:   Wed Jun 28 10:25:28 2023 +0800

    rebase-file4

commit 48d15a4d051c664166822dac0a75b27c524966e5
Author: masecho
Date:   Wed Jun 28 10:25:13 2023 +0800

    rebase-file3

commit b4142d077dd8ceee479259c6047d01287b69af63
Author: masecho
Date:   Wed Jun 28 10:24:53 2023 +0800

    rebase-file2

commit b990df0ef6c86bd92641029dbb874997bf3b1e5b
Author: masecho
Date:   Wed Jun 28 10:24:38 2023 +0800

    rebase-file1

使用指令 git rebase -i HEAD~3 就可以更改最近的三条提交

git rebase -i HEAD~3

出现 Vi 编辑器画面 (注意这里是按照时间顺序排列,与 git log 是按照时间倒序排列相反的)

使用 DD 可以剪切,使用 P 可以粘贴

pick 48d15a4 rebase-file3
pick 1609eb7 rebase-file4
pick c4abcc2 rebase-file5

# Rebase b4142d0..907196f onto b4142d0 (5 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
#                    commit's log message, unless -C is used, in which case
#                    keep only this commit's message; -c is same as -C but
#                    opens the editor
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
#         create a merge commit using the original merge commit's
#         message (or the oneline, if no original merge commit was
#         specified); use -c <commit> to reword the commit message
# u, update-ref <ref> = track a placeholder for the <ref> to be updated
#                       to this position in the new commits. The <ref> is
.git/rebase-merge/git-rebase-todo [unix] (11:09 28/06/2023)

修改成 4/5/3,(1/2/3/4/5)=>(1/2/4/5/3)

pick 1609eb7 rebase-file4
pick c4abcc2 rebase-file5
pick 48d15a4 rebase-file3

之后按"esc",输入 ":wq"就可以保存,若不想更改,输入 ":qa!",结果如下:

commit 48d15a4d051c664166822dac0a75b27c524966e5
Author: masecho
Date:   Wed Jun 28 10:25:13 2023 +0800

    rebase-file3

commit c4abcc2dcbb785042ef7fc5df50a36c68cca78e4
Author: masecho
Date:   Wed Jun 28 10:25:41 2023 +0800

    rebase-file5

commit 1609eb7f6b3eb2e90f1c5f564222cad746ad9b02
Author: masecho
Date:   Wed Jun 28 10:25:28 2023 +0800

    rebase-file4
Contributors: masecho, --