Git是目前世界上最先进的分布式版本控制系统。
工作原理 / 流程:
- Workspace:工作区
- Index / Stage:暂存区
- Repository:仓库区(或本地仓库)
- Remote:远程仓库
Git 的安装、配置就不说了,直接说如何操作。
一,创建版本库
sulei32@ZBMAC-C02D42W8M ~ % cd Project
sulei32@ZBMAC-C02D42W8M Project % mkdir test
sulei32@ZBMAC-C02D42W8M Project % cd test
sulei32@ZBMAC-C02D42W8M test % pwd
/Users/sulei32/Project/test
首先用终端创建了一个test目录,然后通过命令 git init 把这个目录变成git可以管理的仓库。
sulei32@ZBMAC-C02D42W8M test % git init
Initialized empty Git repository in /Users/sulei32/Project/test/.git/
这时候test目录下会多了一个.git的隐藏目录,这个目录是Git来跟踪管理版本的,没事千万不要手动乱改这个目录里面的文件,否则,会把 git 仓库给破坏了。
sulei32@ZBMAC-C02D42W8M test % touch readme.txt
sulei32@ZBMAC-C02D42W8M test % ls
readme.txt
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
readme.txt
nothing added to commit but untracked files present (use "git add" to track)
在 test 目录下创建一个 readme.txt 文件,然后输入 git status 命令,命令告诉我们 readme.txt 是一个Untracked(未追踪)的文件,提醒我们用 git add 命令把文件暂存区。
sulei32@ZBMAC-C02D42W8M test % git add readme.txt
sulei32@ZBMAC-C02D42W8M test % git commit -m "提交readme.txt"
[master (root-commit) 752c1d2] 提交readme.txt
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 readme.txt
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
nothing to commit, working tree clean
- 用 git add 命令把 readme.txt 文件添加到暂存区。
- 用 git commit 命令把 readme.txt 文件提交到仓库。
- 用 git status 查看,发现没有需要提交的文件了。
下面我们来修改 readme.txt 文件的内容,输入一行11111,继续使用 git status 来查看下结果
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
上面的结果告诉我们,readme.txt 文件已被 modified(修改),但是未被提交。我们可以使用 git add 命令把文件的修改添加到暂存区;也可以使用 git restore 命令 discard(丢弃)本次的修改。我们先执行 git add 命令,然后在使用 git status 来查看结果
sulei32@ZBMAC-C02D42W8M test % git add .
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: readme.txt
上面结果告诉我们,本次对 readme.txt 文件的修改已经在暂存区了,可以使用 git commit 命令提交到仓库;也可以使用 git restore --staged 命令 unstage(从暂存区中移除)本次的修改。我们先执行 git commit 命令,然后在使用 git status 来查看结果,搞定。
sulei32@ZBMAC-C02D42W8M test % git commit -m "提交11111"
[master 0daf0ad] 提交11111
1 file changed, 1 insertion(+)
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
nothing to commit, working tree clean
二,版本回退:
我们先对 readme.txt 文件进行两次修改,分别增加两行22222和333333。
sulei32@ZBMAC-C02D42W8M test % open readme.txt
sulei32@ZBMAC-C02D42W8M test % git add .
sulei32@ZBMAC-C02D42W8M test % git commit -m "22222"
[master 10e281f] 22222
1 file changed, 2 insertions(+), 1 deletion(-)
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
nothing to commit, working tree clean
sulei32@ZBMAC-C02D42W8M test % open readme.txt
sulei32@ZBMAC-C02D42W8M test % git add .
sulei32@ZBMAC-C02D42W8M test % git commit -m "333333"
[master af5b641] 333333
1 file changed, 2 insertions(+), 1 deletion(-)
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
nothing to commit, working tree clean
sulei32@ZBMAC-C02D42W8M test % more readme.txt
11111
22222
33333
好了,现在我们已经对 readme.txt 文件做了三次修改了,那么我现在想查看下历史记录,如何查呢?我们可以使用 git log 命令。
sulei32@ZBMAC-C02D42W8M test % git log
commit af5b641fa767d1cc7d61554a5847bf5873f771e8 (HEAD -> master)
Author: sulei32 <sulei32@jd.com>
Date: Thu Mar 11 18:43:18 2021 +0800
333333
commit 10e281f6a0050c9fec649849d73ac85956912947
Author: sulei32 <sulei32@jd.com>
Date: Thu Mar 11 18:42:42 2021 +0800
22222
commit 0daf0ad5bc634278607ffd0bf0b05a66734aa191
Author: sulei32 <sulei32@jd.com>
Date: Thu Mar 11 18:09:16 2021 +0800
提交11111
commit 752c1d20a3254e375b7e0a6b91b3509115d0a0ef
Author: sulei32 <sulei32@jd.com>
Date: Thu Mar 11 17:48:17 2021 +0800
提交readme.txt
git log 命令是从最近到最远的显示日志,日志信息分别为:每次提交的版本号、提交人、提交时间、提交的日志信息。如果现在我们想进行版本回退操作,我要回退到上个版本,有两种方法:第一种,使用 git reset --hard HEAD^ 命令, 那么如果要回退到上上个版本只需把HEAD^ 改成 HEAD^^,如果要回退到前100个版本的话,我们可以使用更简便命令 git reset --hard HEAD~100 。第二种,使用 git reset --hard 版本号 命令,版本号可以使用 git reflog 命令来获取。我们分别来试下这两种方法:
sulei32@ZBMAC-C02D42W8M test % more readme.txt
11111
22222
33333
sulei32@ZBMAC-C02D42W8M test % git reset --hard HEAD^
HEAD is now at 10e281f 22222
sulei32@ZBMAC-C02D42W8M test % more readme.txt
11111
22222
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
nothing to commit, working tree clean
sulei32@ZBMAC-C02D42W8M test % git reflog
3ff090b (HEAD -> master) HEAD@{0}: commit: 33333
10e281f HEAD@{1}: reset: moving to HEAD^
af5b641 HEAD@{2}: commit: 333333
10e281f HEAD@{3}: commit: 22222
0daf0ad HEAD@{4}: commit: 提交11111
752c1d2 HEAD@{5}: commit (initial): 提交readme.txt
sulei32@ZBMAC-C02D42W8M test % git reset --hard 10e281f
HEAD is now at 10e281f 22222
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
nothing to commit, working tree clean
sulei32@ZBMAC-C02D42W8M test % more readme.txt
11111
22222
sulei32@ZBMAC-C02D42W8M test %
这时还有个问题,如果我已经回退到22222的版本了,但是现在我想再次回退到33333版本,那怎么办呢?我们可以用上面的第二种办法解决这个问题。
sulei32@ZBMAC-C02D42W8M test % more readme.txt
11111
22222
sulei32@ZBMAC-C02D42W8M test % git reflog
10e281f (HEAD -> master) HEAD@{0}: reset: moving to 10e281f
3ff090b HEAD@{1}: commit: 33333
10e281f (HEAD -> master) HEAD@{2}: reset: moving to HEAD^
af5b641 HEAD@{3}: commit: 333333
10e281f (HEAD -> master) HEAD@{4}: commit: 22222
0daf0ad HEAD@{5}: commit: 提交11111
752c1d2 HEAD@{6}: commit (initial): 提交readme.txt
sulei32@ZBMAC-C02D42W8M test % git reset --hard 3ff090b
HEAD is now at 3ff090b 33333
sulei32@ZBMAC-C02D42W8M test % more readme.txt
11111
22222
33333
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
nothing to commit, working tree clean
三,撤销修改
撤销修改操作分为三种情况:1,修改没有 add ;2,修改已经 add,但是没有 commit;3,修改已经 commit;4,修改已经被 push 到远程仓库。比如我在 readme.txt 文件里加了一行44444,现在我想撤销本次修改,针对上面的四种情况,我们分别处理。
第一种情况,使用 git restore 命令。
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
sulei32@ZBMAC-C02D42W8M test % git restore .
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
nothing to commit, working tree clean
sulei32@ZBMAC-C02D42W8M test % more readme.txt
11111
22222
33333
第二种情况,分两步:1,使用 git restore --staged 命令,把已经 add 的修改变成没有add;2,用第一种方法撤销修改。
sulei32@ZBMAC-C02D42W8M test % git add .
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: readme.txt
sulei32@ZBMAC-C02D42W8M test % git restore --staged .
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
第三种情况,使用上一节的版本回退方法。
第四种情况,放到下一节讲,我们现在还没有远程仓库。
四,远程仓库
上面说的都是本地仓库(Repository),本节开始学习远程仓库(Remote)并且把Repository和Remote进行同步。现在的情景是:我们已经在本地创建了一个Git仓库,又想在github创建一个Git仓库,并且希望这两个仓库进行远程同步,这样github的仓库可以作为备份,又可以其他人通过该仓库来协作。
1,创建远程仓库
首先,登录github上,然后在右上角找到“create a new repo”创建一个新的仓库,然后在Repository name 填入test,其他保持默认设置,点击“Create repository”按钮,就成功地创建了一个新的Git仓库。
目前,在GitHub上的这个test仓库还是空的,GitHub告诉我们:
- 用命令行创建新的本地仓库,然后把本地仓库的内容推送(push)到远程仓库。
- 如果已经存在本地仓库,可以直接把本地仓库的内容推送(push)到远程仓库。
- 可以从其他远程仓库导入(import)代码。
我们已经有本地仓库了,所以使用第二种方法。
sulei32@ZBMAC-C02D42W8M test % git remote add origin https://github.com/sulei007/test.git
sulei32@ZBMAC-C02D42W8M test % git push -u origin master
Enumerating objects: 12, done.
Counting objects: 100% (12/12), done.
Delta compression using up to 12 threads
Compressing objects: 100% (4/4), done.
Writing objects: 100% (12/12), 866 bytes | 866.00 KiB/s, done.
Total 12 (delta 0), reused 0 (delta 0)
To https://github.com/sulei007/test.git
* [new branch] master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
sulei32@ZBMAC-C02D42W8M test %
把本地库的内容推送到远程,使用 git push命令,实际上是把当前分支master推送到远程。
由于远程库是空的,我们第一次推送master分支时,加上了 –u参数,Git不但会把本地的master分支内容推送到远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
现在我们往 readme.txt 里添加一行44444,git add 和 git commit 后,使用 git status 命令查看结果:
sulei32@ZBMAC-C02D42W8M test % git add .
sulei32@ZBMAC-C02D42W8M test % git commit -m "提交44444"
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
nothing to commit, working tree clean
请注意,git commit 命令后,git status 的提示信息跟前面几节只有本地仓库的时候不一样了,git 提示我们,本地分支的内容领先远程分支,建议我们用 git push 命令把本地的修改提交到远程仓库。
sulei32@ZBMAC-C02D42W8M test % git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 258 bytes | 258.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/sulei007/test.git
53aa659..1ddd36d master -> master
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
2,如何从远程库克隆?
我们在Project下,新建一个test2的空目录,然后在test2目录下,使用 git clone 命令。
sulei32@ZBMAC-C02D42W8M Project % mkdir test2
sulei32@ZBMAC-C02D42W8M Project % ls
dy-pos-android test test2
sulei32@ZBMAC-C02D42W8M Project % cd test2
sulei32@ZBMAC-C02D42W8M test2 % ls
sulei32@ZBMAC-C02D42W8M test2 % git clone https://github.com/sulei007/test.git
Cloning into 'test'...
remote: Enumerating objects: 15, done.
remote: Counting objects: 100% (15/15), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 15 (delta 0), reused 15 (delta 0), pack-reused 0
Unpacking objects: 100% (15/15), done.
sulei32@ZBMAC-C02D42W8M test2 % ls
test
可以看到,远程仓库的test目录,已经被克隆到本地的test2目录下了。
五、操作分支
按照上面的操作,我们创建的仓库只有一个分支 master 分支,也叫主分支,可以使用 git branch 命令查看本地分支,会列出所有的本地分支,当前分支前面会添加一个星号。我们如果要查看远程分支,可以使用 git branch -a 命令,远程分支带有 remotes/origin 前缀。
sulei32@ZBMAC-C02D42W8M test % git branch
* master
下面,我们来创建dev本地分支,然后切换到dev本地分支上。可以使用 git checkout -b dev 命令,这条命令相当于如下两条命令: git branch dev 和 git checkout dev。
sulei32@ZBMAC-C02D42W8M test % git checkout -b dev
Switched to a new branch 'dev'
sulei32@ZBMAC-C02D42W8M test % git branch
* dev
master
sulei32@ZBMAC-C02D42W8M test % git branch -a
* dev
master
remotes/origin/master
上面结果可以看到,dev是本地分支,我们需要把dev本地分支推送到远程仓库。
sulei32@ZBMAC-C02D42W8M test % git push -u origin dev
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Writing objects: 100% (3/3), 259 bytes | 259.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
remote:
remote: Create a pull request for 'dev' on GitHub by visiting:
remote: https://github.com/sulei007/test/pull/new/dev
remote:
To https://github.com/sulei007/test.git
* [new branch] dev -> dev
Branch 'dev' set up to track remote branch 'dev' from 'origin'.
sulei32@ZBMAC-C02D42W8M test % git branch -a
* dev
master
remotes/origin/dev
remotes/origin/master
然后我们在 dev 分支下的 readme.txt 新增一行55555,然后 git add 、git commit 、git push 到远程 dev 分支,然后切换到 master 分支,我们发现 master 下的 readme.txt 文件没有55555这一行。
sulei32@ZBMAC-C02D42W8M test % git branch
* dev
master
sulei32@ZBMAC-C02D42W8M test % more readme.txt
11111
22222
33333
44444
55555
sulei32@ZBMAC-C02D42W8M test % git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'.
sulei32@ZBMAC-C02D42W8M test % more readme.txt
11111
22222
33333
44444
现在我们可以把 dev 分支上的内容合并到 master 分支上了,在 master 分支上,使用 git merge dev 命令,可以看到在 master 分支下的 readme.txt 文件里也有55555这一行了,然后我们把修改 push 到远程仓库。
sulei32@ZBMAC-C02D42W8M test % git branch
dev
* master
sulei32@ZBMAC-C02D42W8M test % git merge dev
Updating 1ddd36d..0c5fe0b
Fast-forward
readme.txt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
sulei32@ZBMAC-C02D42W8M test % more readme.txt
11111
22222
33333
44444
55555
sulei32@ZBMAC-C02D42W8M test % git push
Total 0 (delta 0), reused 0 (delta 0)
To https://github.com/sulei007/test.git
1ddd36d..0c5fe0b master -> master
总结创建与合并分支命令如下:
- 查看分支:git branch,git branch -a
- 创建本地分支:git branch name
- 切换本地分支:git checkout name
- 创建+切换本地分支:git checkout –b name
- 合并某本地分支到当前本地分支:git merge name
- 删除本地分支:git branch –d name,只能删除非当前分支。
- 拉去远程分支到本地:git checkout -b xxxx(本地分支名称)yyyy(远程分支的名称)
在合并分支的时候,有可能会出现冲突,比如我们在 dev 分支上加了一行aaaaaa,提交;然后在 master 分支上也加了一行bbbbbb,当把 dev 分支合并到 master 分支上时,就会合并失败,提示有冲突(conflict)。
sulei32@ZBMAC-C02D42W8M test % git merge dev
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.
sulei32@ZBMAC-C02D42W8M test % more readme.txt
11111
22222
33333
44444
55555
<<<<<<< HEAD
bbbbb
=======
aaaaa
>>>>>>> dev
Git 用<<<<<<<,=======,>>>>>>>标记出不同分支的内容,其中<<<<<<< HEAD 是指 master 分支修改的内容,>>>>>>> dev 是指 dev 上修改的内容,我们可以修改下,然后提交。
sulei32@ZBMAC-C02D42W8M test % more readme.txt
11111
22222
33333
44444
55555
bbbbb
aaaaa
sulei32@ZBMAC-C02D42W8M test % git add .
sulei32@ZBMAC-C02D42W8M test % git commit -m "merge冲突"
[master 0241f2b] merge冲突
sulei32@ZBMAC-C02D42W8M test % git push
Enumerating objects: 11, done.
Counting objects: 100% (11/11), done.
Delta compression using up to 12 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (9/9), 699 bytes | 699.00 KiB/s, done.
Total 9 (delta 1), reused 0 (delta 0)
remote: Resolving deltas: 100% (1/1), done.
To https://github.com/sulei007/test.git
0c5fe0b..0241f2b master -> master
下面我们说下上一节课遗留的问题,我现在把 dev 分支合并到 master 分支上了,并且已经 push 到远程仓库了,但是现在我想撤销这次合并,master 分支上的 readme.txt 文件不要55555这一行了。分两步:1,在 master 本地分支上使用 git reset --hard HEAD^ 命令,回退到上个版本;2,使用 git push -f 命令强制提交,远程 master 分支的将强制更新到本地 reset 的版本。
sulei32@ZBMAC-C02D42W8M test % git reset --hard HEAD^
HEAD is now at 1ddd36d 提交44444
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
(use "git pull" to update your local branch)
nothing to commit, working tree clean
sulei32@ZBMAC-C02D42W8M test % git push
To https://github.com/sulei007/test.git
! [rejected] master -> master (non-fast-forward)
error: failed to push some refs to 'https://github.com/sulei007/test.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
sulei32@ZBMAC-C02D42W8M test % git push -f
Total 0 (delta 0), reused 0 (delta 0)
To https://github.com/sulei007/test.git
+ 0c5fe0b...1ddd36d master -> master (forced update)
sulei32@ZBMAC-C02D42W8M test % git status
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
上面结果可以看到,如果不加 -f 参数,git push 命令是会报错的,原因是本地分支的版本低于远程分支的版本。
六,修复bug
在开发中,会经常碰到bug,那么有了bug就需要修复,在Git中,分支是很强大的,每个bug都可以通过一个临时分支来修复,修复完成后,合并分支,然后将临时的分支删除掉。
比如在开发中接到一个 bug ,我们可以创建一个 fix 分支来修复它,但是,当前的 dev 分支上的工作还没有提交。
sulei32@ZBMAC-C02D42W8M test % git status
On branch dev
Your branch is up to date with 'origin/dev'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
sulei32@ZBMAC-C02D42W8M test % git checkout master
error: Your local changes to the following files would be overwritten by checkout:
readme.txt
Please commit your changes or stash them before you switch branches.
Aborting
这个时候我们 dev 分支上的开发工作还没有完成,我们并不想提交,但是修复 bug 肯定更为紧急,怎么办呢?看上面的提示信息,可以使用 git stash 命令,把当前的修改 ”缓存起来”,等修复完bug后继续工作。
sulei32@ZBMAC-C02D42W8M test % git stash
Saved working directory and index state WIP on dev: 0c5fe0b 提交55555
sulei32@ZBMAC-C02D42W8M test % git status
On branch dev
Your branch is up to date with 'origin/dev'.
nothing to commit, working tree clean
sulei32@ZBMAC-C02D42W8M test % git checkout master
Switched to branch 'master'
Your branch is up to date with 'origin/master'
上面结果显示,我们成功的切换到了 master 分支,那么我们就可以在 master 分支上新建一个 fix 分支,在 fix 分支里修复完bug,把 fix 分支合并到 master 分支上,然后删除 fix 分支,这样bug 就修复好了。我们再切换到 dev 分支,用 git stash list 命令来查看缓存列表,然后用 git stash pop 命令来取出缓存,该命令会从列表头部依次取出缓存,且在取出某条缓存的同时也会把这条缓存从列表中删除了。
sulei32@ZBMAC-C02D42W8M test % git stash list
stash@{0}: WIP on dev: 0c5fe0b 提交55555
sulei32@ZBMAC-C02D42W8M test % open readme.txt
sulei32@ZBMAC-C02D42W8M test % git stash pop
On branch dev
Your branch is up to date with 'origin/dev'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (515083e1a15171c2b23e689e1a334f3f9232e606)