欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Git 使用札记

程序员文章站 2022-07-01 15:19:15
...

Git 是一个优秀的版本控制工具,可以有效、快速的处理大大小小的项目版本管理。日常使用掌握如下图所示的工作流程和命令就足够了。

Git 使用札记

但要熟练地使用 git,只掌握上图中的命令是不够的。以下是我在使用 git 过程中收集的比较有用的操作和命令。

配置

Git 的配置文件为 .gitconfig,它在用户主目录下~/.gitconfig,针对当前用户是全局的,同时每个项目目录下也可以有一个局部的配置文件,仅针对当前项目有效。

# 显示当前的配置
git config --list

# 配置文本编辑器
git config --global core.editor vim

# 编辑配置文件
git config -e --global

# 设置提交代码时的用户信息
git config --global user.name "[name]"
git config --global user.email "[email address]"

# 设置换行符转化规则
git config --global core.autocrlf input
git config --global core.safecrlf true

# 保持文件权限设置
git config --global core.filemode true

# 设置 merge branch 时使用 --rebase
git config --global branch.autosetuprebase always
git config --global pull.rebase true

# 配置命令别名
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.ci commit
git config --global alias.br branch

# 记录手动输入的账号和密码 
git config --global credential.helper store

# 配置全局的 ignore 文件为 ~/.gitignore
git config --global core.excludesfile ~/.gitignore

仓库初始化

# 初始化当前目录为一个 git 仓库
git init .

# 新建一个目录并初始化为 git 仓库
git init <Directory>

# 初始化一个裸的仓库,即没有工作区,通常用于作为远程仓库
git init --bare

# 从远程克隆一个仓库到本地
git clone <url>

查看提交历史

# 显示当前分支的版本历史
git log

# 显示当前分支的版本历史,同时显示每个提交发生变化的文件
git log --stat

# 按作者查看版本历史,作者名不需要精确匹配,仅需要包含即可
git log --author="Huoty"

# 按 commit 信息查看,-i 参数可以忽略大小写
git log --grep="xxx"

# 按日期查看,可用 --after 或 --before 来按日期赛选
git log --after="2018-04-01" --before="2018-04-15"

# 显示每次提交中被修改文件简要的增改行数统计
git log --stat

# 简短的显示文件的变化状态统计,不显示变化的文件名
git log --shortstat

# 展开显示每次提交的内容差异,-num 可只显示开头的提交,如 -2 表示仅显示最近的两次更新
git log -p

# 展示每个提交所在的分支及其分化衍合情况
git log --pretty=fuller --graph

# 把每个版本历史信息压缩到一行
git --no-pager log --oneline --decorate

# 显示某次提交的内容 
git show <Commit ID>

单文件历史

# 显示某个文件的历史版本的所有改动
git log --follow -p [file]
git whatchanged -p [file]

# 显示指定文件的每一行内容的作者和修改时间
git blame [file]

shortlog

git shortlog 将 commit 按照作者分组,显示每个 commit 的第一行描述,这样便于看到每个作者都做哪些工作。可以认为它是一个特殊版本的 git log,其对各个作者的提交进行汇总。

# 统计每个作者的提交情况
git shortlog -sn

-s 参数省略每次 commit 的注释,仅仅返回一个简单的统计。
-n 参数按照 commit 数量从多到少的顺利对用户进行排序

差异比较

# 查看工作区域暂存区的差异
git diff

# 查看暂存区与版本库的差异
git diff --cached
git diff --staged

# 查看 工作区、暂存区 与 HEAD 的差异
git diff HEAD  

# 查看 branch2 分支的哪些改动不存在于 branch1 上
git diff branch1..branch2 

# 查看当前分支与主分支最近公共祖先的差异
git diff master...

暂存工作区

git stash 可以在不提交当前修改的情况下将工作区的修改进行暂存,等到合适的时候再恢复。它的用途是暂停当前工作,临时去干别的事。

# 保存当前的工作进度。会分别对暂存区和工作区的状态进行保存
git stash

# 显示进度列表。git stash 可以多次保存工作进度,并可在恢复时候进行选择
git stash list

# 恢复暂存。如果不使用任何参数,会恢复最新保存的工作进度
git stash pop [--index] [<stash>]

# 恢复暂存,但不删除进度
git stash apply [--index] [<stash>]

# 清除所有暂存
git stash clear

找回丢失的提交

git reflog 可以列出 HEAD 曾经指向过的一系列 commit。reflog (relog is Reference logs),它是一个分支的顶端或者每次更新到本地仓库的引用的一个记录,简单的说就是 HEAD 曾经指向过引用。该命令可用于在错误操作时找回丢失的提交。

如果引起 commit 丢失的原因并没有记录在 reflog 中,则使用 git fsck 工具,该工具会检查仓库的数据完整性。如果指定 --full 选项,可以显示所有未被其他对象引用 (指向) 的对象。指定 --lost-found 会尝试搜索被删除的 commit。

git reflog

git fsck --full

git fsck --lost-found

cherry-pick

git cherry-pick 能够将其他分支上的某个或者多个提交应用到当前分支中,而不必将整个分支的内容都合并到当前分支。

# 将任意分支中的 commit 应用到当前分支
git cherry-pick <Commit ID>  

如果 cherry-pick 多个连续 commit,应注意如下几点:

  • 1. 使用两个点(..)来指明 commit 的区间
  • 2. git cherry-pick A..B 中,A 的 commit 应该在 B 之前
  • 3. 默认的选取范围不包含 A 但包含 B,类似于数学上开闭区间 (A,B]
  • 4. 若要包含 A,请使用 git cherry-pick A^..B,即可做到 [A,B]

revert

git revert 用于只撤销历史上的某个或者某几个提交,而不改变前后的提交历史,并且将撤销操作作为一次最新的提交。

# 撤销 commit 并新生成一个 commit
git revert <Commit ID>

一定要理解,revert 操作是提交一个新的版本,将需要 revert 的版本的内容再反向修改回去,版本会递增,之前的提交历史不会有任务改动。

注意与 reset 的区分,revert 应该被称之为 恢复,而 reset 则是回退的意思,它会修改历史版本。

整理提交历史

# 修改最新一个提交
git commit --amend

# 整理某一提交之后的所有提交
git rebase -i <Commit ID>

# 修改历史上任意提交
git commit --fixup <Commit ID>
git commit --squash <Commit ID>
  • git commit --amend:

该命令可以修改最新一次提交,包括 commit message 和 内容。如果你在提交工作后发现有哪里不对,可以继续在工作区进行修改,然后使用该命令对最新一次错误提交进行补充或者修改。

  • git rebase -i:

rebase 被理解为 变基,所以变基就是相对于一个基点来进行调整。-i 即 --interactive 的简写。该命令需要指定一个 commit 作为基点,在该 commit 之后的提交都可在交互式的文本编辑器中进行修改。其有如下可操作的命令:

p, pick = use commit
r, reword = use commit, but edit the commit message
e, edit = use commit, but stop for amending
s, squash = use commit, but meld into previous commit
f, fixup = like "squash", but discard this commit's log message
x, exec = run command (the rest of the line) using shell                                                                                                  
d, drop = remove commit

他们的含义分别是:

pick:正常选中,即保持不变,这个默认的操作命令
reword:修改该提交的 commit message
edit:此时 rebase 时会暂停,允许你修改这个提交,可用于拆分提交
squash:把内容和 commit message 都合并到前一个提交
fixup:与 squash 类似,内容会被合并到前一个提交中,但 commit message 会被抛弃
exec:执行其他 shell 命令
drop:删除该提交不再保留,或者直接将改行删掉也是同样的效果
  • git commit [--fixup | --squash]:

该命令用于修改历史上任意一个提交,如果发现之前的某个提交有错误,可以说用该命令。使用 --fixup会自动多出一个 fixup! 开头的提交。而使用 --squash 则允许编辑 message,然后生成一个 squash! 开头的提交。最后使用 git rebase -i --autosquash 则可以完成对历史提交的修改。假设需要修改的 commit 为 abc123,则操作命令如下所示:

git commit --fixup abc123
git rebase -i --autosquash abc123^

撤销暂存区

如果执行 git add 操作后发现修改的内容有误,需要重新修改时,可以使用 reset 撤销提交到暂存区的修改到工作区中。

# 撤销某一文件
git rest file
git reset HEAD file

# 撤销整个暂存区
get reset
git reset HEAD

代码回滚

# 撤销指定文件在工作区中的修改
git checkout [file]

# 建立一个临时分支,并将 HEAD 指向指定的 commit
git checkout [commit]

# 恢复某个文件的内容为指定的 commit 或者 branch
git checkout [commit/branch] [file]

# 从其他分支检出某个目录下的所有内容到当前分支
git checkout [branch] -- [path] 

# 撤销工作区中当前目录下的所有修改
git checkout .

# 恢复添加到暂存区的内容到工作中
git reset [file]

# 重置暂存区与工作区为指定的 commit
git reset --hard [commit]

# 重置当前分支的指针为指定 commit,同时重置暂存区,但工作区不变
git reset [commit]

# 保持暂存区和工作区不变,但重置当前 HEAD 为指定的 commit
git reset --keep [commit]

# 撤销指定 commit 的修改并生成一个新的 commit
git revert [commit]

拯救出错的 rebase 操作

如果分支上有新的提交还没 push,可以用 reflog 查看操作历史,然后用 reset 回退:

$ git reflog

b710729 aaa@qq.com{0}: rebase: some commit
5ad7c1c aaa@qq.com{1}: rebase: another commit
deafcbf aaa@qq.com{2}: checkout: moving from master to my-branch
...

$ git reset aaa@qq.com{2} --hard

如果分支上没有提交,或者不关心这个分支上新的提交,只是想做 rebase 操作,那么可以先切换到别的分支,把 rebase 出问题的分支删掉再重新 rebase:

$ git checkout my-branch
$ git rebase master
// not happy with result
$ git checkout master
$ git branch -D my-branch
$ git checkout my-branch

或者更简单一些,让 HEAD 指向远程分支的 HEAD:

$ git reset --hard origin/my-branch

分支替换

即一个分支完全替换另一个分支。如果要替换远程分支,可以直接将本地分支强行推送到远程分支:

git push -f origin develop:master

即把本地的 develop 分支强制(-f)推送到远程 master。但这样做之后,本地的 master 分支还是旧的,如果希望本地的 master 分支与远程的 master 分之同步,则需要先替换到本地的 master 分支,再推送到远程仓库:

# 切换到旧的分支
$ git checkout master  

# 将本地的旧分支 master 重置成 develop
$ git reset --hard develop  

# 再推送到远程仓库
$ git push origin master --force  

何时保留分支历史

合并其他分支代码 git merge 模式采用 fast-forward 方式进行合并。fast-forward 方式就是当条件允许的时候,git 直接把 HEAD 指针指向合并分支的头,完成合并。属于 “快进方式”,不过这种情况如果删除分支,则会丢失分支信息。因为在这个过程中没有创建 commit。

在 merge 其他分支时,建议保留其他分支的历史,即期望 merge 过来的分支具体都干了啥。所以建议将 merge 加上 --no-ff 参数。--no-ff 指的是强行关闭 fast-forward 方式,而刻意制造分叉。区别如下图所以:

Git 使用札记

在 pull 代码时,实际上 pull 操作做了两个操作,即 fetch 和 merge。也就是说,git 先取得远程仓库的代码,然后将远程分支与本地合并,这样会使分支分叉,如果仓库有很多协作开发者,则分支历史看上去会很乱。所以建议 pull 时不采用 merge 方式,而是用 rebase 的方式,即 pull 加上 --rebase 参数。可以通过配置使 pull 默认采用 rebase:

git config --global pull.rebase true

# 将提交约线图平坦化
git pull --rebase

# 刻意制造分叉
git merge --no-ff 

打上标签

# 列出所有标签
git tag

# 新建带附注标签
git tag -a [tagname] -m [message]

# 删除本地标签
git tag -d [tagname]

# 查看查看信息
git show [tagname]

# 推送标签
git push [remote] [tagname]

# 提交所有变迁
git push [remote] --tags

# 新建一个分支,指向某个标签
git checkout -b [branch] [tagname]

删除远程分支有三种方式:

# 1. 使用参数 --delete:
git push origin --delete tag <tagname>

# 2. 相当于推送一个空分支到远程分支:
git push origin :<tagname>

# 3. 先删除本地 tag,在推送一个空的 tag 到远程仓库:
git tag -d
git push origin :refs/tags/<tagname>

远程仓库

# 显示所有远程仓库
git remote -v

# 显示某个远程仓库的信息
git remote show [remote]

# 增加一个新的远程仓库,并命名
git remote add [remote] [url]

# 修改远程仓库地址
git remote set-url [remote] [url]

# 拉取远程仓库的所有变动
git fetch [remote]
git remote update [remote]

# 拉取远程仓库中的指定分支并与本地分支合并
git pull [remote] [branch]

# 上传本地指定分支到远程仓库
git push [remote] [branch]

# 强行推送当前分支到远程仓库,即使有冲突
git push [remote] [branch] --force

# 推送所有分支到远程仓库
git push [remote] --all

指定 sshkey 访问远程仓库

有时候,由于可能会在机器上维护多个 sshkey,那么某些地方用默认 key 可能会有问题。这时就需要做些配置来满足需求。

编辑文件:

vi ~/.ssh/config

加入以下内容:

Host git.company.com
  User git
  IdentityFile /Users/guanliyuan/.ssh/test
  IdentitiesOnly yes

其中:

  • git.company.com 是你的远程仓库域名
  • User git 这样配置表示这是给 git 命令使用的
  • IdentityFile 表示私钥文件地址
  • IdentitiesOnly 配置为 yes,表示只使用这里的 key,防止使用默认的

清理工作区

使用 git clean 可以清除工作区未被添加到索引中的文件。参数说明如下:

# 以下参数必须提供一个
-i --interactive 交互方式显示将要完成的操作和清理文件
-n --dry-run 仅打印要删除的文件,但不实际执行删除操作
-f --force 确定移除,即无论如何都强制移除

# 以下参数为可选参数
-d 连同目录一起删除
-x 删除所有未跟踪的文件,不管是否包含在 .gitignore 文件中
-X 仅删除 .gitignore 文件中包含的文件

其他

# 交互式的一段一段的添加修改到工作区
git add -p

# 仅将新增加的文件路径添加到暂存区,但不添加内容
git add -N

# 删除本地已经合并的分支
git branch -d [branch]

# 强制删除本地分支,即使没被合并
git branch -D [branch]

# 在提交的时候指定作者信息
git commit --author="name <aaa@qq.com>" ...

# 生成一个可供发布的压缩包
git archive
git archive --format=[archive format] \
    --prefix=[archive prefix path] [branch] \
    > [archive path]

本文转自:http://blog.konghy.cn/2018/05/06/git-notes/

相关标签: Git