Linux / 工具 · 2022/12/27 0

Git上手笔记

花半天时间上手了 Git,跟正则表达式一样,大部分功能用不到,但用得到的地方非常有用。我用它搭配 Github、软链接、硬链接管理 Windows 里各种软件的配置文件、游戏存档文件。博客文章是用 markdown 本地写好再发出来的,准备把文章、配图用 git 同步到一个 github仓库做备份,也在摸索 Github Pages :)

安装

1. Linux安装

首先,输入git看看系统有没有安装Git:

$ git

在 Ubuntu 或 Debian下,输入:

apt-get install git

2. windows 安装

打开 Git Bash,输入:

$ git config --global user.name "Your Name" 
$ git config --global user.email "[email protected]"

创建版本库(repository)

首先,选择一个合适的地方,创建一个空目录:

mkdir learngit
cd learngit
pwd
#=> C:\Hi\gitone

pwd命令显示当前目录,在我的PC上,这个仓库位于C:\Hi\gitone

第二步,通过git init命令把这个目录变成Git可以管理的仓库.

git init

这时 gitone 就变成了Git仓库,并且多了一个跟踪管理版本库的.git目录。

把文件添加到版本库

需要注意:版本控制系统只能跟踪本文文件的改动,比如第二个版本在第一个版本的第5行加了一个单词,不能跟踪非文本文件(图片、视频等)的变动内容

在 gitone 文件夹下新建readme.txt,输入:

Git is a version control system.
Git is free software.

保存后,在Git Bash里用git add命令,将文件添加到暂存区:

git add readme.txt

git commit 将文件提交到版本库:

$ git commit -m "注释"

查看工作区状态:

git status

会做两次对比,工作区vs暂存区 及 暂存区vs版本库,并将两次对比的结果输出。

比较文件修改内容

git diff <file>

git diff 比较工作区和暂存区的文件,如果暂存区为空,则结果等同于 git diff HEAD,即比较工作区和版本库的差异。如果要比较暂存区和版本库的差异用 git diff --cached

版本管理

log 可以告诉我们版本历史,会显示commit_id、创建人、邮件、版本时间、注释,当前版本后显示有(HEAD -> master)

git log <file>

按行显示版本历史,只显示 commit_id、注释

git log --pretty=oneline <file>

HEAD 表示当前版本,HEAD^ 表示上一个版本,HEAD^^ 表示上上版本,HEAD~100 表示往上100个版本。HEAD 是方便用于指代版本号的相对名称,也可以用 commit_id 来指代版本,commit_id 只需要用前几位字符,不需要写全。

将工作区当前版本的文件回退到上一个版本:

git reset --hard HEAD^

再用 git log 查看版本,会发现之前的版本不见了,如果要回到之前的版本,在命令行窗口还没有关掉时,可以用之前版本的 commit_id 恢复

git reset --hard commit_id

如果已经关闭命令行,可以用 reflog 查询曾经 commit 的 commit_id,reset 到该版本

git reflog

工作区和暂存区

git 的本地基本流程:

        工作区  →  暂存区  →  版本库

               add       commit

               reset

                      checkout/restore

               reset HEAD/restore --staged

暂存区文件是 stage 或 index,Git 会自动创建第一个分支 master,git add 把工作区文件添加到暂存区,git commit将暂存区的内容提交到当前分支。

撤销修改

将工作区文件回到最近一次 git add 或 git commit 时的状态:

git checkout -- <file>

如果文件已经 commit,我们可以将工作区文件 git reset –hard 到某个版本。

如果文件已经 add 但未 commit,只是想把暂存区修改回退到工作区:

git reset HEAD <file>

回退后工作区恢复到上次 add 的版本,暂存区被清空

git checkout 可以用 git restore 代替

git reset HEAD 可以用 git restore –staged 代替

删除文件

git rm <file>
git commit -m "注释"

添加远程库

在Github添加 repository 后,在本地新建存储库linux并运行命令:

git remote add origin [email protected]:iliyz/linux.git

远程仓库的默认名称是origin,也可以改成别的

关联远程库后,使用命令第一次推送 master 分支的所有内容,-u会把本地当前 master分支的所有内容推送到远程库的master分支,还会把本地的master分支和远程的master分支关联起来

git push -u origin master  

此后,每次本地提交后,只要有必要,就可以使用命令推送最新修改:

git push origin master

查看远程库信息

git remote -v

解除本地和远程的绑定关系

git remote rm origin

解除绑定后对本地文件的操作不影响远程库

从远程库克隆

git clone [email protected]:iliyz/linux.git

Git 支持多种协议,默认的git://使用ssh,也可以使用 https 等其它协议.

git clone 会在本地新建目录。

创建 SSH Key

在用户主目录下,看看有没有.ssh目录,有的话看目录下有没有id_rsaid_rsa.pub这两个文件. 如果没有,打开 Shell(windows下打开 Git Bash),创建 SSH Key:

ssh-keygen -t rsa -C "[email protected]"

一路回车默认值即可,如果对保密要求较高,可以设置密码.

id_rsa是私钥,不能泄露出去,id_rsa.pub 是公钥,可以放心告诉任何人.

分支管理

创建 dev 分支

git branch dev

查看分支

git branch

切换分支(也可以用 git checkout)

git switch <name>

切换分支需要当前分支下工作区、暂存区与分支存储库的提交相同,即 git diff HEAD 无差异,否则可能报错无法跳转,分支跳转后,工作区目录会回复到当前分支最近一次提交下的目录情况。

需要注意,切换分支时未add或未commit的内容会一并带过去,因为未add的内容不明确属于任何一个分支。

创建并切换到新创建的分支(也可以用 git checkout -b)

git switch -c <name>

合并某分支到当前分支

git merge <name>

删除分支

git branch -d <name>

解决冲突

当两个分支合并有冲突时,Git 会在冲突文件中用 <<<<<<<=======>>>>>>>标记出不同分支的内容,必须手动解决冲突后再提交

可以用 git status 查看冲突的文件,用 git log加上参数可以查看分支的合并情况

git log --graph --pretty=oneline --abbrev-commit

分支策略

Git默认使用Fast forward模式合并分支,这种模式下,删除分支后,会丢掉分支信息。

​ A—B—C dev

/ \

D—————E main 合并后 D—————E main

可以强制禁用 Fast forward模式,Git就会在merge时生成一个新的commit,并合并分支历史信息。--no-ff模式仅当合并的历史是当前历史的后代时有效。

git merge --no-ff -m "merge with no-ff" dev

​ A—B—C dev

/ \

D—————E main 合并后 D–A–B–C–E main

常用的策略是,main 仅用于发布新版本,项目开发在 dev 分支,每个人都有自己的分支并不时向 dev合并。

BUG分支

另一种常见情况是正在 dev 分支,突然被提交了一个需要马上修复的 bug,而当前 dev 分支的工作只进行到一部分,还不能提交。

这时可以用到 stash功能,储藏当前工作现场,等以后恢复现场后继续工作:

git stash

再用 git status查看工作区,是干净的

然后开始修复bug,假设在 main分支做修复

git switch main
git switch -c issue-101
# 修复并提交
git switch main
git merge --no-ff -m "merged bug fix 101" issue-101

然后回到 dev 分支继续工作

git switch dev
git stash list # 查看储藏的工作现场

恢复工作现场,但不删除储藏的工作现场

git stash apply    #如果有多个stash:git stash apply stash@{0}

删除保留的储藏的工作现场

git stash drop

恢复工作现场,并删除储藏的工作现场

git stash pop

这个bug已经在 main 分支做了修复,但其实在 dev 上也存在,假设要在 dev 分支修复相同的 bug,只需要把 issue-101 这个提交所做的修改复制到 dev 分支

git cherry-pick commit_id

Feature分支

如果要丢弃一个没有被合并过的分支,可以强行删除

git branch -D <name>

多人协作

查看远程库信息,显示了可以抓取和推送的 origin 地址。如果没有推送权限就看不到 push 地址

git remote -v

推送时,要指定本地分支。这样,Git就会把该分支推送到远程库对应的远程分支上,比如:

git push origin main

当协作者从远程库 clone 时,默认情况下只能看到本地的 main 分支

假设协作者创建了远程库的 dev 分支来进行开发

 git checkout -b dev origin/dev

协作者就可以在 dev 上继续修改,时不时将 dev分支 push 到远程

假设协作者和你试图推送的提交有冲突,需要先从 origin/dev pull 最新的提交,然后,在本地合并,解决冲突,再推送:

git pull

提示no tracking information是因为没有指定本地 dev 分支与远程 origin/dev 分支的链接,设置链接:

git branch --set-upstream-to=origin/dev dev

再次 pull,成功后手动解决冲突,再 push

git push origin dev

Rebase

git rebase

rebase操作可以把本地未push的分叉提交历史整理成直线,在查看历史提交的变化时更容易。

rebase会改变提交历史记录,这会影响到别人使用这一远程仓库,只建议对尚未推送或分享给别人的本地修改执行变基操作清理历史。

标签管理

标签比40位的 commit_id 更易读,比如 v1.0、v2.0,它相当于 commit_id 的别名

默认为当前分支最新提交的 commit 打标签,比如 v1.0

git tag v1.0

为指定的提交打标签

git tag v0.9 <commit_id>

为为指定的提交打标签并创建说明

git tag -a v0.8 -m "说明" <commit_id>

查看所有标签

git tag

查看标签信息

git show <tagname>

删除标签

git tag -d v1.0

创建的标签都只存储在本地,不会自动推送到远程,如果要推送到远程

git push origin <tagname>

或将全部尚未推送到远程的本地标签推送到远程:

git push origin --tags

删除远程标签,先删除本地标签,再 push 删除远程标签

git tag -d v1.0
git push origin :refs/tags/v1.0

自定义 Git

代码高亮

git config --global color.ui true

忽略特殊文件

git 会忽略工作区根目录下 .gitignore 文件中列出的文件

Github已经准备了各种不同环境下的配置文件,见github/gitignore

主要为系统自动生成的文件,如缩略图、desktop.ini等;编译生成的中间文件、可执行文件等;带有敏感信息的文件,如存放口令的配置文件

有时候想添加一个文件到 git失败,原因是 The following paths are ignored by one of your .gitignore files:,要强制添加可以用-f

git add -f <file>

.ignore 的写法

# 排除所有 . 开头的隐藏文件,所有 .class文件,等等
.*
*.class
dist
build
db.ini
Desktop.ini
Thumbs.db

#不排除 .gitignore 和 App.class
!.gitignore
!App.class

搭建Git服务器

自建非常简单,大规模管理公钥用Gitosis,需要控制权限用Gitolite

参考资料