Git 实用指南

9次阅读

共计 4505 个字符,预计需要花费 12 分钟才能阅读完成。

个人整理的一些常用的 Git 概念和命令集合,方便速查和快速解决某些场景下的问题,覆盖了日常开发和协同工作下的一部分场景,不只是命令行的介绍。欢迎关注语雀原文,持续更新!

精简入门
1、克隆仓库
克隆仓库会下载仓库完整的文件、分支和历史记录。
git clone [<options>] [–] <repo> [<dir>]
# 克隆完整的仓库到 ./git-learning 目录下
git clone git@github.com:x-cold/git-learning.git
# 只克隆 dev 分支到 ./dev 目录下
git clone -b dev git@github.com:x-cold/git-learning.git dev
2、将文件变更记录写入到本地的索引库
git add [<options>] [–] <pathspec>…
# 添加当前目录下所有文件
git add .
# 添加部分文件
git add src/ app/ index.js
3、提交变更到工作区
git commit [<options>] [–] <pathspec>…
# 最普通的提交
git commit -m “feat: support canvas”
# 修改当前的 commit message
git commit –amend
# 重置当前的 commit author 和 message
git commit –amend –reset-author
4、推送代码到远程仓库
git push [<options>] [<repository> [<refspec>…]]
# 提交本地仓库当前分支到远程仓库的 master 分支
git push origin master
# 提交本地仓库 dev 分支到远程的 master 分支
git push origin master:dev

聊聊设计
图像来自维基百科
Git 是一个分布式的版本控制工具,因此远程和本地可以视为两个独立的 Git 仓库。上图是一张经典的 Git 中的数据流与存储级别的介绍,其中储存级别主要包含几部分:

工作区 (Working Files),指的是我们时刻在编辑的文件的目录,通常来说我们修改文件都是在工作区体现的
暂存区(Stage),暂存将本地的修改,然后提交到本地仓库
本地仓库(Local)
远程仓库(Remote)

由此不难看出整体的数据流动,就是一条从:工作区 -> 暂存区 -> 本地仓库 -> 远程仓库 的双向数据流通道。

常用命令

git init
创建一个空白的 git 仓库
git init

git add
git add [<options>] [–] <pathspec>…

git commit
git commit [<options>] [–] <pathspec>…

git remote
remote 指的是本地的 git 仓库关联的远程 git 仓库。
1、查看远程仓库信息
git remote
2、看远程仓库详细信息
git remote -v
3、删除远程仓库
git remote remove <name>
# 移除名字为 origin 的远程仓库
git remote remove origin
4、添加远程仓库
 git remote add [-t <branch>] [-m <master>] [-f] [–tags | –no-tags] [–mirror=<fetch|push>] <name> <url>
git remote origin git@github.com:x-cold/git-learning.git

git branch
1、列出本地存在的分支
git branch
2、列出远程分支
git branch -r
3、列出本地和远程分支
git branch -a
4、创建本地分支
git branch [branchName]  (remoteBranch)
# 基于远程仓库的 dev 分支,创建本地仓库的 feature/canvas 分支
git branch feature/canvas dev
5、分支重命名
git branch [<options>] (-m | -M) [<old-branch>] <new-branch>
# 修改 feature/canvas 分支名为 feature/canvas2
git branch -M feature/canvas feature/canvas2
6、删除本地分支
git branch -d | -D [branchName]
7、删除远程分支
git branch [<options>] [-r] (-d | -D) <branch-name>.
# 删除 feature/canvas2 分支
git branch -d feature/canvas2
8、设置默认上游及分支
git branch –set-upstream <localBranch> <remote>/<remoteBranch>
# 以后只需要在 dev 分支执行 git push (无需额外的参数) 就可以提交到 origin/dev
git branch –set-upstream dev origin/dev

git checkout
检出分支: git checkout [<options>] <branch>
# 切换当前分支到 dev 分支
git checkout dev
# 基于当前分支创建 test 分支,并且将当前分支切换到 test 分支
git checkout -b test
除开用于分支切换,checkout 还可以用于恢复未添加到本地工作区,但是被修改过的文件。**
# 将 index.js 恢复到当前 commit 的内容
git checkout index.js

git merge
合并分支: git merge [<options>] [<commit>…]
# 合并远程仓库的 master 分支到当前分支
git merge origin/master

git rebase
变基,是一种常用且有风险的操作,会改变提交历史,谨慎使用!
git rebase
while(存在冲突) {
git status
找到当前冲突文件,编辑解决冲突
git add -u
git rebase –continue
if(git rebase –abort)
break;
}

git cherry-pick
魔法级的命令,cherry-pick 可以提取 N 个的提交记录,合入稳定版本的分支上。git cherry-pick [<options>] <commit-ish>…
# 挑选 371c2 单个提交记录,合入当前分支
git cherry-pick 371c2
# 挑选出 371c2 到 971209 的所有提交记录,并合入当前分支
git cherry-pick 371c2…971209

git push
推送到远程仓库,同步本地仓库的提交历史到远程仓库
git push [<options>] [<repository> [<refspec>…]]
# 提交本地仓库当前分支到远程仓库的 master 分支
git push origin master
# 提交本地仓库 dev 分支到远程的 master 分支
git push origin master:dev
# 提交单个 tag
git push origin publish/1.0.0
# 提交所有 tag
git push origin –tags

git pull
拉取远程分支,同步远程仓库的提交历史到本地仓库
git pull [<options>] [<repository> [<refspec>…]]
# 通常来说,默认的 pull 行为等同于 git fetch + git merge
# 下面这行命令等同于 git fetch origin master && git merge origin/master
git pull origin master

# 也可以通过变基的方式来拉取代码,这样分支模型不容易受到影响
# 下面这行命令等同于 git fetch origin master && git rebase origin/master
git pull –rebase origin master

git tag
1、创建 tag
git tag -a v1.1.0 -m “”
2、查看 tag
git tag
3、推送到远程
git push origin –tags
4、删除本地 tag
git tag -d v1.0.0
5、删除远程 tag
git push origin :refs/tags/v1.0.0

.git 仓库元数据
每一个 git 的代码仓库目录下,都会有一个 .git 的文件夹,其中包含的重要文件包含以下:

文件 / 文件夹
含义

config*
配置文件

description
描述,仅供 Git Web 程序使用

HEAD
当前被检出的分支

index
暂存区信息

hooks/
客户端或服务端的钩子脚本(hook scripts)

info/
全局性排除(global exclude)文件,不希望被记录在 .gitignore 文件中的忽略模式(ignored patterns)

objects/
所有数据内容

refs/
数据(分支)的提交对象的指针

进阶技巧

修改 commit 历史
使用 git rebase 进行历史修改,假定修改最近 3 条历史,操作步骤如下:
1、git rebase -i HEAD~3
运行此命令会提供一个提交列表,如下所示,其中 commit 记录是时间逆序排列的;
pick f7f3f6d changed my name a bit
pick 310154e updated README formatting and added blame
pick a5f4a0d added cat-file

# Rebase 710f0f8..a5f4a0d onto 710f0f8
#
# Commands:
# p, pick = use commit
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#
2、编辑上述列表文件,在需要更改的 commit 前,将 pick 修改为 edit,如果需要压缩,可设置为 squash 保存退出,进入到 rebase 流程;
3、通过 git commit –amend –author 对历史记录依次修改和持续进行 rebase;

添加指定文件
git ls-files src/ | grep ‘\.css$’ | xargs git add

删除所有 commit 中的某些文件
# 删除文件
git filter-branch –force –index-filter ‘git rm –cached –ignore-unmatch -r build’ –prune-empty –tag-name-filter cat — –all

# 触发 GC
git reflog expire –expire=now –all && git gc –prune=now –aggressive

附录

githug, 一个专门为 git 学习路径设计的游戏

awesome-git-addons, git 命令行工具扩展的合集

git-tips, 常用使用场景和技巧集合

lazygit, 懒人专用的 git 命令行程序

其他用途

issue 评论

gitment, github issue 社会化评论插件

gittalk, github issue 社会化评论插件,感觉稍微好看一点点

正文完
 0

Git 使用指南

9次阅读

共计 9254 个字符,预计需要花费 24 分钟才能阅读完成。

git init
初始化
git init
git clone
克隆
git clone [远端仓库] [目标文件夹名称(默认:远端仓库名)]
git log
查看历史提交:按时间先后顺序显示到 [校验和] 为止
git log [校验和(默认:最新提交的校验和)]
选项:

–oneline:精简至单行版本

–stat:增加文件修改信息

-p:忽略空格引起的不同

-w:忽略空格引起的不同

–all –graph:用分支图显示所有历史提交

–author=”[作者]”:显示作者包含 [文本] 的历史提交

–grep=”[文本]”:显示内容包含 [文本] 的历史提交

希望选项 –all –graph 成为命令 git log 的默认参数?
在文件.bashrc 中加入:
git() {
if [“$1” = “log”]
then
command git log –all –graph “${@:2}”;
else
command git “$@”;
fi;
}
Enabling git log parameters by default
git shortlog
查看历史提交:按作者分类显示
git shortlog
git show
查看 [校验和] 提交的所有信息
git show [校验和(默认:最新提交的校验和)]
查看附注标签(Annotated Tag)的内容
git tag [标签名]
选项同 git log。
git status
查看当前状态
git status
git diff
查看尚未加入暂存区的改动
git diff
git add
将文件从工作区添加到暂存区
git add [文件名 1 文件名 2 文件名 3 …]
将所有文件(文件.gitignore 指示的除外)从工作区添加到暂存区
git add .
git rm
将文件从暂存区移回到工作区
git rm –cached [文件名 1 文件名 2 文件名 3 …]
git commit
将暂存区提交到本地仓库

会进入一个 vim 窗口,在此输入提交信息。
所有以 #开头的行都会被忽略。
对于空行的问题无需纠结,因为 git 会忽略所有不必要的空行。

git commit
键入提交信息并提交
git commit -m [提交信息]
更新最近的提交(内容和信息)
git commit –amend
如修改了提交内容,建议先执行:
git add .
提交信息的书写规范(Udacity)
Udacity Git Commit Message Style Guide

The goal is that each commit has a single focus. Each commit should record a single-unit change. Now this can be a bit subjective (which is totally fine), but each commit should make a change to just one aspect of the project.
Conversely, a commit shouldn’t include unrelated changes – changes to the sidebar and rewording content in the footer. These two aren’t related to each other and shouldn’t be included in the same commit. Work on one change first, commit that, and then change the second one. That way, if it turns out that one change had a bug and you have to undo it, you don’t have to undo the other change too.
The best way that I’ve found to think about what should be in a commit is to think, “What if all changes introduced in this commit were erased?”. If a commit were erased, it should only remove one thing.
DO:

do keep the message short (less than 60-ish characters)
do explain what the commit does (not how or why!)

DO NOT:

do not explain why the changes are made (more on this below)
do not explain how the changes are made (that’s what git log -p is for!)

do not use the word “and”

if you have to use “and”, your commit message is probably doing too many changes – break the changes into separate commits
e.g. “make the background color pink and increase the size of the sidebar”

git tag
查看所有标签
git tag
添加轻便标签(Lightweight Tag)
git tag [标签名] [校验和(默认:最新提交的校验和)]
添加附注标签(Annotated Tag)

会进入一个 vim 窗口,在此输入标签内容。
所有以 #开头的行都会被忽略。对于空行的问题无需纠结,
因为 git 会忽略所有不必要的空行。

git tag -a [标签名] [校验和(默认:最新提交的校验和)]
键入附注标签的内容并提交
git tag -a [标签名] [校验和(默认:最新提交的校验和)] -m [附注标签的内容]
删除本地标签
git tag -d [标签名 1 标签名 2 标签名 3 …]
删除指定远端标签
git push [远端名] -d refs/tags/[标签名]
git push [远端名] :refs/tags/[标签名]
推送所有本地标签到远端
git push [远端名] –tag
从远端拉取所有标签到本地
git fetch [远端名] –tag
(在远端唯一的情况下,[远端名]可以省略。)
git checkout
切换
git branch [校验和 / 标签名 / 分支名]
恢复工作区中的某个文件到上次提交的状态
git checkout — [文件名]
恢复工作区中的全部文件到上次提交的状态
git checkout — .
git branch
查看所有本地分支
git branch
查看所有远端分支
git branch -r
新建分支
git branch [分支名] [校验和(默认:最新提交的校验和)]
删除分支
git branch -d [分支名 1 分支名 2 分支名 3 …]
删除分支的前提条件是:

该分支不是当前分支。
该分支上的最新提交已经合并到另一分支上(如需忽略本条件,请使用 - D 选项。警告:尚未合并的所有提交将会全部丢失!)。

git merge
将 [分支名] 合并到当前分支
git merge [分支名]
合并操作操作会因为冲突(conflict)而中止,条件是:参与合并的两个分支上,各存在一个提交,它们修改了同一个行范围。
如需撤销合并操作,请使用命令 git merge –abort。
冲突的处理方法(Udacity)

Merge Conflict Output Explained
The output that shows in the Terminal is:
$ git merge heading-update
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
Notice that right after the git merge heading-update command, it tries merging the file that was changed on both branches (index.html), but that there was a conflict. Also, notice that it tells you what happened – “Automatic merge failed; fix conflicts and then commit the result”.
Remember our good friend git status? Well he’ll come in really handy when working with merge conflicts.
$ git status
On branch master
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: index.html
The git status output tells us to that the merge conflict is inside index.html. So check out that file in your code editor!

Merge Conflict Indicators Explanation

<<<<<<< HEAD everything below this line (until the next indicator) shows you what’s on the current branch

||||||| merged common ancestors everything below this line (until the next indicator) shows you what the original lines were

> > > > > > > heading-update is the ending indicator of what’s on the branch that’s being merged in (in this case, the heading-update branch)

Resolving A Merge Conflict
Git is using the merge conflict indicators to show you what lines caused the merge conflict on the two different branches as well as what the original line used to have. So to resolve a merge conflict, you need to:

choose which line(s) to keep
remove all lines with indicators

For some reason, I’m not happy with the word “Crusade” right now, but “Quest” isn’t all that exciting either. How about “Adventurous Quest” as a heading?!?

Commit Merge Conflict
Once you’ve removed all lines with merge conflict indicators and have selected what heading you want to use, just save the file, add it to the staging index, and commit it! Just like with a regular merge, this will pop open your code editor for you to supply a commit message. Just like before, it’s common to use the provided merge commit message, so after the editor opens, just close it to use the provided commit message.
And that’s it! Merge conflicts really aren’t all that challenging once you understand what the merge conflict indicators are showing you.
If a merge conflict occurs in a file and you edit the file, save it, stage it, and commit it but forget to remove the merge conflict indicators, Git will commit the lines with the merge conflict indicators, because they’re just regular characters.

git revert
git revert [校验和]
设 [校验和] 提交的前一个提交为 PREVIOUS,修改的行范围是 range。
该命令的作用是回滚 range 范围内的修改,将会产生一个新的提交。
回滚操作操作会因为冲突而中止,条件是:如果 range 范围内的某一部分在 [校验和] 提交的某个后续提交中被修改。
如需撤销回归操作,请使用命令 git revert –abort。
冲突的处理方法与 git merge 相同。
git reset
git reset [校验和]
该命令的作用是回到 [校验和] 提交的状态,即撤销 [校验和] 提交的后续提交的所有修改 modification。
选项:

–mixed:将 modification 移入工作区(默认)

–soft:将 modification 移入暂存区

–hard:忽略(删除)modification

执行 git reset 之后,[校验和]提交的后续提交未被删除。
可以通过下列命令查看这些不属于任何分支的提交:

git relog
git fsck –unreachable –no-reflog

(具体使用方法详见后文。)
可以通过切换到提交、新建分支、合并到现有分支的方法恢复这些提交(即回滚 git reset 操作)。
相对引用(Udacity)

Relative Commit References
You already know that you can reference commits by their SHA, by tags, branches, and the special HEAD pointer. Sometimes that’s not enough, though. There will be times when you’ll want to reference a commit relative to another commit. For example, there will be times where you’ll want to tell Git about the commit that’s one before the current commit…or two before the current commit. There are special characters called “Ancestry References” that we can use to tell Git about these relative references. Those characters are:

^ – indicates the parent commit

~ – indicates the first parent commit

Here’s how we can refer to previous commits:

the parent commit – the following indicate the parent commit of the current commit
HEAD^

HEAD~
HEAD~1

the grandparent commit – the following indicate the grandparent commit of the current commit

HEAD^^
HEAD~2

the great-grandparent commit – the following indicate the great-grandparent commit of the current commit

HEAD^^^
HEAD~3

The main difference between the ^ and the ~ is when a commit is created from a merge. A merge commit has two parents. With a merge commit, the ^ reference is used to indicate the first parent of the commit while ^2 indicates the second parent.

The first parent is the branch you were on when you ran git merge
The second parent is the branch that was merged in.

git remote
查看所有远端仓库的 URL
git remote -v
关联到远端仓库:[远端仓库的别名]通常被设为 origin
git remote add [远端仓库的别名] [远端仓库的 URL]
取消与远端仓库的关联
git remote remove [远端仓库的别名]
git push
这里仅讨论同时满足下列条件的情形:

本地分支和远程分支同名。
单一远端,通常是 origin。

将本地分支推送到远端(完整命令)
git push [远端名] [本地分支] [远端分支]
省略 [远端分支] 的情形:将 [本地分支] 推送到“与之存在追踪关系的”远程分支(通常两者同名),如果该远程分支不存在,则会被新建。
git push [远端名] [本地分支]
可以为该命令添加 - u 参数,表示将远端分支设为 [本地分支] 的 upstream branch。
git push -u [远端名] [本地分支]
执行上述命令后,就可以在切换到相应分支 git checkout [本地分支]之后,直接使用命令 git push 了。
git push
选项 –all 表示推送所有分支到远端,通常添加选项 - u 以设定所有本地分支的 upstream branch。
git push -u –all
删除远端分支
git push [远端名] -d [远端分支]
git push [远端名] :[远端分支]
如果出现各种原因导致命令 git push 不能成功执行(例如:删除并重建本地分支、合并本地的 commit),可以使用选项 - f 进行强行(覆盖)推送。请注意:不要在多人合作的远端分支中慎用,原因参见:

团队开发里频繁使用 git rebase 来保持树的整洁好吗?
git 如何撤销一次 remote 的 master commit?

git push -f
git pull
这里仅讨论同时满足下列条件的情形:

本地分支和远程分支同名。
单一远端,通常是 origin。

该命令的用途是:取回远程主机某个分支的更新,再与本地的指定分支合并。因为远端分支 origin/master 通常是本地分支 master 的更新,所以合并操作只是移动 master 引用的位置,通常难以察觉。
将远端分支拉取到本地(完整命令,多用于创建和远端分支对应的本地分支)。
git pull [远端名] [远端分支]:[本地分支]
在满足下列条件的前提下,可以直接使用命令 git pull:

已使用 git checkout 将该对应的本地分支切换为当前分支。
该分支的 upstream branch 已设置。具体方法为 git branch –set-upstream-to=origin/[远端分支] [本地分支]

git pull
git fetch
这里仅讨论同时满足下列条件的情形:

本地分支和远程分支同名。
单一远端,通常是 origin。

该命令的用途是:取回远程主机某个分支的更新,但是不与本地分支合并。
git fetch [远端名] [远端分支]:[本地分支]
在满足下列条件的前提下,可以直接使用命令 git pull:

已使用 git checkout 将该对应的本地分支切换为当前分支。
该分支的 upstream branch 已设置。具体方法为 git branch –set-upstream-to=origin/[远端分支] [本地分支]

git fetch
区别于命令 git pull:

命令 git fetch 让用户决定怎样合并远端分支和本地分支。
命令 git pull 自动进行分支合并,在某些情况下可能无法成功执行。

problem
本地分支和远端分支被同时更新。
更新前:
master
|
remote a — 3 — d — f — e — 7

============================================================

origin/master
|
local a — 3 — d — f — e — 7
|
master
更新后:
master
|
remote a — 3 — d — f — e — 7 — 8

============================================================

origin/master
|
local a — 3 — d — f — e — 7 — b
|
master
Solution
首先先使用命令 git fetch,获取远端分支更新。
master
|
remote a — 3 — d — f — e — 7 — 8

============================================================

origin/master
|
8
/
local a — 3 — d — f — e — 7 — b
|
master
再使用命令 git merge origin/master,将远端分支合并到本地分支。
master
|
remote a — 3 — d — f — e — 7 — 8

============================================================

origin/master
|
8
/ \
local a — 3 — d — f — e — 7 — b — 4
|
master
最后使用命令 git push,推送本地分支到远端分支。
master
|
8 |
/ \ |
remote a — 3 — d — f — e — 7 — 8 — 4

============================================================

origin/master
|
8 |
/ \ |
local a — 3 — d — f — e — 7 — b — 4
|
master
其他
https://www.worldhello.net/go…
查看(90 天内的)操作历史
git reflog
查看不可达的(unreachable)对象,包括但不限于:

暂存区操作时引入的临时对象(以 unreachable blob 开头)
不在分支上的提交(以 unreachable commit 开头)

git fsck –unreachable –no-reflog
清除不可达的对象(通常为:暂存区操作时引入的临时对象)

如果对象 git reflog 命令中可见,那么 git prune 就不能清除它。
若一定要清除,则需先使用 git reflog expire –expire=now –all 清除所有历史(不推荐)。

git prune
优化仓库
git gc

正文完
 0