版本控制工具——Git常用操作(上)

7次阅读

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

本文由云 + 社区发表作者:工程师小熊

摘要:用了很久的 Git 和 svn, 由于总是眼高手低,没能静下心来写这些程序员日常开发最常用的知识点。现在准备开一个专题,专门来总结一下版本控制工具,让我们从 git 开始。完成本系列博客的阅读以后,你将掌握 git 的基本概念与 git 的基本命令,可以在本地随心所欲的完成代码的提交撤销保存修改等操作、可以流畅的参与多人协作,本文致力于快速的入门,如果涉及到更高级的功能需要进行更深一步的学习。
本文核心点:

Git 的基本概念
一个人使用 Git 时的代码版本控制 –(提交、拉代码、分支操作)
多人合作时的代码版本控制 –(合并冲突、暂存代码)

什么是 Git
简介
git 是世界上目前最先进的分布式版本控制系统, 致力于团队、个人进行项目版本管理,完美的解决难以比较代码、难以合并代码、难以取消修改、难以在写当前代码的过程中保存未完成的修改去修改线上版本的 bug 等的痛点。
git 是一个非常强大的工具,但作为一个 git 使用者来说,不用完全学习 Git 的知识点与命令,因为有的命令的使用频率非常的低甚至数年都不会用到,让我们来由浅入深进行学习。
git 的历史
git 是 linux 的创始人 linus,在付费版本控制工具 BitMover 收回对 Linux 社区免费使用权利的时候,一怒之下花费两个星期的时间写出来的。(牛笔的人)
开始
安装 git
选择自己的操作系统对应的 git 版本安装,安装成功后运行 git version 后,输出 git 版本则安装正确。
git 官方:https://git-scm.com/downloads
配置用户信息
使用 git config 命令来配置用户名和邮箱
git config –global user.name “pzqu”
git config –global user.email pzqu@example.com
如果用了 –global 选项,那么更改的配置文件就是位于你用户主目录下的那个,以后你所有的项目都会默认使用这里配置的用户信息。如果要在某个特定的项目中使用其他名字或者电邮,只要去掉 –global 选项重新配置即可,新的设定保存在当前项目的 .git/config 文件里。
使用 git config user.name 和 git config user.email 来检查是否成功,也可以直接用 git config –list 来列出全部 git 配置信息来查看

创建 git 托管的项目
假如我们创建一个项目叫 make_money,先创建一个文件夹叫 make_money,再使用 git init 命令创建 git 项目。
# pzqu @ pzqu-pc in ~/Documents/code/test [0:05:29]
$ mkdir make_money

# pzqu @ pzqu-pc in ~/Documents/code/test [0:06:24]
$ ls
make_money

# pzqu @ pzqu-pc in ~/Documents/code/test [0:06:29]
$ cd make_money

# pzqu @ pzqu-pc in ~/Documents/code/test/make_money [0:07:10]
$ git init
Initialized empty Git repository in /Users/pzqu/Documents/code/test/make_money/.git/

# pzqu @ pzqu-pc in ~/Documents/code/test/make_money on git:master o [0:07:12]
$ ls -al
total 0
drwxr-xr-x 3 pzqu staff 96 11 7 00:07 .
drwxr-xr-x 3 pzqu staff 96 11 7 00:06 ..
drwxr-xr-x 9 pzqu staff 288 11 7 00:07 .git
创建成功以后,会出现一个叫.git 的隐藏文件夹,这个就是你的 git 仓库,以后所有的 git 操作历史提交记录信息就全部记录在此了,只要这个文件夹在就可以记住我们的全部 git 操作
工作区和暂存区
在使用 git 的时候还要清楚暂存区和工作区的含义,参考廖雪峰的官方网站 -git 篇 - 工作区和暂存区
常见情况
提交代码
新文件与修改
# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [11:37:50]
$ ls
README.md

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [11:42:02]
$ touch file1.txt

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master x [11:42:15]
$ git add file1.txt

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master x [11:42:23]
$ git status
On branch master
Your branch is up to date with ‘origin/master’.

Changes to be committed:
(use “git reset HEAD <file>…” to unstage)

new file: file1.txt
# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master x [11:56:38]
$ git commit -m “[+]add new file1.txt”
[master 66cc488] [+]add new file1.txt
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file1.txt
上图操作包含:

创建新文件 file1.txt
add 添加修改的内容到索引
status 查看修改的内容
commit 把索引提交到本地分支

git add .:监控工作区的状态树,此命令会把工作时的所有变化提交到暂存区,包括文件内容修改 (modified) 以及新文件(new),但不包括被删除的文件。
git add -u:他仅监控已经被 add 的文件(即 tracked file),他会将被修改的文件提交到暂存区。add -u 不会提交新文件(untracked file)。(git add –update 的缩写)
git add -A:是上面两个功能的合集(git add –all 的缩写)
upload successful
git show 列出最近一次的提交
对于 commit:像这样,你不断对文件进行修改,然后不断提交修改到版本库里,就好比玩 RPG 游戏时,每通过一关就会自动把游戏状态存盘,如果某一关没过去,你还可以选择读取前一关的状态。有些时候,在打 Boss 之前,你会手动存盘,以便万一打 Boss 失败了,可以从最近的地方重新开始。Git 也是一样,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在 Git 中被称为 commit。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个 commit 恢复,然后继续工作,而不是把几个月的工作成果全部丢失。
删除文件
# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [12:55:24]
$ ls
README.md file1.txt

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [12:55:25]
$ git rm file1.txt
rm ‘file1.txt’

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master x [12:55:30]
$ ls
README.md

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master x [12:55:32]
$ git status
On branch master
Your branch is ahead of ‘origin/master’ by 1 commit.
(use “git push” to publish your local commits)

Changes to be committed:
(use “git reset HEAD <file>…” to unstage)

deleted: file1.txt

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master x [12:55:40] C:128
$ git commit -m “[-]delete file1.txt”
[master e278392] [-]delete file1.txt
1 file changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 file1.txt
上图操作包含:

创建新文件 file1.txt
git rm 删除 file1.txt 文件
status 查看修改的内容
commit 把索引提交到本地分支

tip1: 如果没有用 git rm 删除文件,在本地删除文件后,git add 一下再提交可以达到同样的效果
tip2: 要是你加班太晚,头晕不小心删除了不想删除的文件怎么办?见
下一篇:版本控制工具——Git 常用操作(下)- 后悔药
拉代码
方法一 pull
# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [17:01:13]
$ git pull
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From github.com:pzqu/git_test
5fd4d8f..7b54a8a master -> origin/master
Merge made by the ‘recursive’ strategy.
share_file.txt | 1 +
1 file changed, 1 insertion(+)
create mode 100644 share_file.txt
上图命令:
git pull
查看本地仓库变化 git log
upload successful
上图可以看到向远程仓库 pull 的时候,出现了两个新的 commit,commit 7b54a8ae74… 的提交信息为 Create share_file.txt, 另一个 commit fdbb19cf4c51770 的提交信息为 Merge branch ‘master’ of github.com:pzqu/git_test。事实上主线只有一个提交,为什么会出现这种情况? 是因为 pull 其实会做两个操作

拉远程仓库代码到本地
自动与当前分支合并并生成一个合并成功的提交

注意这里的第二个个步骤如果远程有人和你改了同一个文件就会出现一个冲突,这个时候 git 会提示你哪些文件有冲突,手动改了再提交一次就可以了。详情见合并冲突
方法二 fetch
我在远程修改了文件,向 share_file.txt 加了一行内容 tom modify,此时拉代码。
# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [21:07:21]
$ git fetch

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:master o [21:08:43]
$ git rebase origin/master
First, rewinding head to replay your work on top of it…
Applying: [+]add new file1.txt
Applying: [-]delete file1.txt
上图所示有以下两个操作

fetch 拉取远端代码到本地
rebase 把本地代码提交基于远端分支重新 replay

效果如下:
upload successful
上图是 git log 所输出的提交内容,刚刚 pull 的时候忘记把 pull 自动产生的 merge 提交到远程,rebase 的时候把本地的提交放到了远程提交之后,看起来就是一条直线,比较优雅,也是推荐的方式。
同样的,如果产生了冲突,详情见合并冲突
分支操作
创建分支
分支是多人协同最经典的地方所在,我们来创建一个分支
$ git checkout -b dev/pzqu origin/master
Branch ‘dev/pzqu’ set up to track remote branch ‘master’ from ‘origin’.
Switched to a new branch ‘dev/pzqu’

$ git branch
* dev/pzqu
master

git checkout -b 分支名 其他分支,- b 代表创建并切换到新建的分支,分支名代表新创建的分支叫什么名字,这里叫 dev/pzqu,其他分支代表基于哪一个分支来创建,这里基于远程的 master 分支 origin/master,如果省略则代表基于当前分支

git branch 展示本地的分支情况,加 - a 参数可以展示全部的分支,包括远程分支

* 在分支前,指明了现在所在的分支是 dev/pzqu

切换分支
$ git checkout -b dev/pzqu2
Switched to a new branch ‘dev/pzqu2’

$ git branch
dev/pzqu
* dev/pzqu2
master

$ git checkout dev/pzqu
Switched to branch ‘dev/pzqu’
Your branch is up to date with ‘origin/master’.

$ git branch
* dev/pzqu
dev/pzqu2
master

基于当前分支创建了一个新的分支并自动切换过去 dev/pzqu2

git checkout 已存在的分支名切换分支回到 dev/pzqu

删除分支
$ git branch
* dev/pzqu
dev/pzqu2
master

$ git branch -D dev/pzqu2
Deleted branch dev/pzqu2 (was 7c9be37).

$ git branch
* dev/pzqu
master

位于 dev/pzqu,删除了 dev/pzqu2 分支
合并冲突
合并同一个分支的冲突(常见)
为了产生一个冲突,我在另一个地方向远程仓库提交了代码,更改 share_file.txt 文件,加了一行内容 tom add for merge,
本地修改同一个文件加了一行 pzqu add for merge,并提交到本地,这样一来,本地和远程仓库的同一个文件就不一样了,一会拉代码一定会产生一个冲突。效果如下:
upload successful

一般 rebase 或 pull 冲突的时候,都会出现提示,然后 git status 会出现上图图示
这个时候不可以进行任何分支切换和 commit 操作,按照他提示进行处理
git status 提示哪个文件是都被修改的,both modified,然后使用编辑器修改该文件,解决冲突
解决完成后,git add 添加该冲突文件
git rebase –continue,并更新 commit message,完成整个 rebase 流程 我们来看看这个冲突的文件:

upload successful
Git 用 <<<<<<<,=======,>>>>>>> 标记出不同分支的内容,我们修改如下后保存:
upload successful
git add 再 git rebase –continue 后完成 rebase,效果如下,再 push 的远程仓库即可
upload successful
合并不同分支的代码产生冲突
关于怎么创建分支与切换分支见创建分支和切换分支, 这里只讨论合并时产生的冲突的情况,我们已经基于 master 分支创建了一个 dev/pzqu 分支
$ git branch
* dev/pzqu
master
切换到 master 分支,加一行 master add for merge 并提交,文件内容如下:
$ cat share_file.txt
tom add
tom modify
tom add for merge
pzqu add for merge
master add for merge
切换到 dev/pzqu 分支,向 share_file.txt 加入一行 dev/pzqu add for merge 并提交,现在 share_file.txt 内容如下:
$ cat share_file.txt
tom add
tom modify
tom add for merge
pzqu add for merge
dev/pzqu add for merge
现在两个分支的同一个文件内容不一样了,现在我们在 dev/pzqu 分支上进行合并:
$ git merge master
Auto-merging share_file.txt
CONFLICT (content): Merge conflict in share_file.txt
Automatic merge failed; fix conflicts and then commit the result.

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:dev/pzqu x [11:17:31] C:1
$ git status
On branch dev/pzqu
Your branch is ahead of ‘origin/master’ by 1 commit.
(use “git push” to publish your local commits)

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: share_file.txt

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

$ cat share_file.txt
tom add
tom modify
tom add for merge
pzqu add for merge
<<<<<<< HEAD
dev/pzqu add for merge
=======
master add for merge
>>>>>>> master
上图出现了一个冲突,是我们意料之中的,修改 share_file.txt 文件,解决此冲突:
$ cat share_file.txt
tom add
tom modify
tom add for merge
pzqu add for merge
dev/pzqu add for merge
master add for merge

$ git add share_file.txt

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:dev/pzqu x [11:22:40]
$ git commit -m “[*]merge master to dev/pzqu”
[dev/pzqu d9e018e] [*]merge master to dev/pzqu

# pzqu @ pzqu-pc in ~/Documents/code/test/git_test on git:dev/pzqu o [11:23:00]
$ git status
On branch dev/pzqu
Your branch is ahead of ‘origin/master’ by 3 commits.
(use “git push” to publish your local commits)

nothing to commit, working tree clean
冲突解决也提交了,看看我们现在的分支内容:
upload successful
上图我们可以看到:

master 分支比远程 origin/master 分支多一次提交,dev/pzqu 分支由于是基于 origin/master 分支,合并了 master 分支的提交和当前 dev/pzqu 分支的提交,超出本地 master 两个提交,致此我们把 master 合并到 dev/pzqu 的操作就完成了。
通常我们开一个新的开发分支是为了在自己的分支上写代码,方便提交也不会把主线弄乱,现在我们用同样的方法将 dev/pzqu 合并到 master 分支,然后把两个分支都提交到远程。

$ git checkout master
Switched to branch ‘master’
Your branch is ahead of ‘origin/master’ by 1 commit.
(use “git push” to publish your local commits)

$ git merge dev/pzqu
Updating 58f047a..d9e018e
Fast-forward
share_file.txt | 1 +
1 file changed, 1 insertion(+)

$ git push origin master
Total 0 (delta 0), reused 0 (delta 0)
To github.com:pzqu/git_test.git
7c9be37..d9e018e master -> master

$ git push origin dev/pzqu
Counting objects: 9, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 887 bytes | 887.00 KiB/s, done.
Total 9 (delta 2), reused 0 (delta 0)
remote: Resolving deltas: 100% (2/2), done.
remote:
remote: Create a pull request for ‘dev/pzqu’ on GitHub by visiting:
remote: https://github.com/pzqu/git_test/pull/new/dev/pzqu
remote:
To github.com:pzqu/git_test.git
* [new branch] dev/pzqu -> dev/pzqu

切换到 master 分支
合并 dev/pzqu 到 master 分支

master 推到远程仓库
如果 dev/pzqu 要保留,就可以推送到远程仓库。

upload successful
现在我们可以看到全部的分支都在一起了,强迫症都舒服了。
暂存代码保存现场
这种情况一般是出现在你正在完成一个功能,但是忽然线上发现了一个 Bug,必须马上开一个新的分支来修复 bug,但是现在的功能没写完不打算提交(commit),现在怎么办??不用怕暂存代码来帮助你。
$ git status
On branch dev/pzqu
Your branch is up to date with ‘origin/master’.

Changes to be committed:
(use “git reset HEAD <file>…” to unstage)

new file: need_stash.txt
modified: share_file.txt

$ git stash
Saved working directory and index state WIP on dev/pzqu: d9e018e [*]merge master to dev/pzqu

$ git stash list
stash@{0}: WIP on dev/pzqu: d9e018e [*]merge master to dev/pzqu

$ git status
On branch dev/pzqu
Your branch is up to date with ‘origin/master’.

nothing to commit, working tree clean

// 省略操作:去创建一个 Bug 分支,修复他并完成与主线的合并,删除 Bug 分支。
// 省略操作:切回来当前分支继续开发
// 下面来恢复现场

$ git stash apply stash@{0}
On branch dev/pzqu
Your branch is up to date with ‘origin/master’.

Changes to be committed:
(use “git reset HEAD <file>…” to unstage)

new file: need_stash.txt

Changes not staged for commit:
(use “git add <file>…” to update what will be committed)
(use “git checkout — <file>…” to discard changes in working directory)

modified: share_file.txt

status 查看到有 2 个文件修改没有提交

stash 把修改放到暂存区,并生成一个 id

stash list 列出暂存区所有内容

stash apply 重新把暂存区内容放到本地

这里的 stash apply 成功的把暂存区的一次暂存恢复到了本地,但是暂存区还有会保存这次暂存,如果想删除这次暂存要用 git stash drop 来删除;也可以用 git stash pop,恢复最后一次暂存的同时把 stash 内容也删了。
$ git stash drop stash@{0}
Dropped stash@{0} (bfdc065df8adc44c8b69fa6826e75c5991e6cad0)

$ git stash list
好了,暂存区清干净了。
​ 注意:要放到暂存区的文件一定要先通过 git add 加到 index
小结
本文阅读结束以后,我们学会了

Git 的基本概念,知道 git 的作用、历史;学会安装配置 Git,使用 Git 创建项目托管以及工作区和暂存区的概念
学会 Git 的本地操作,提交、拉代码、创建切换删除分支操作,
多人合作时的代码版本控制,学会了不同情况下的合并冲突、暂存代码操作

下集预告
Git 常用操作(下)我计划给大家介绍以下点:

后悔药 - 各种后悔操作(撤消 commit, 回滚,回退远程仓库等)
哎呀,提交的时候漏了文件
tag 操作
git 忽略不想提交的文件

注意事项
理论上,git 日常用到的命令是 diff show fetch rebase pull push checkout commit status 等,这些命令都不会导致代码丢失,假如害怕代码丢失,可以预先 commit 一次,再进行修改,但切记
不可使用自己不熟悉的命令 任何命令,不要加上 - f 的强制参数,否则可能导致代码丢失
建议多使用命令行,不要使用图形界面操作
引用
git 官网
廖雪峰的官方网站 -git 篇
hexo 博客部署到 vps
此文已由腾讯云 + 社区在各渠道发布
获取更多新鲜技术干货,可以关注我们腾讯云技术社区 - 云加社区官方号及知乎机构号

正文完
 0