本部分内容来自对于《莫烦Python:Git你的版本管理顾问》的学习,网址为:https://morvanzhou.github.io/ tutorials/ others/ git/。前三节内容略。
4. 记录修改 (log & diff)
1. 修改记录 log
$ git log
2. 查看unstaged
要查看这次还没 add (unstaged) 的修改部分 和上个已经 commit 的文件有何不同, 我们将使用:
$ git diff
3. 查看staged(--cached)
你已经 add 了这次修改, 文件变成了 “可提交状态” (staged), 我们可以在 diff 中添加参数 --cached 来查看修改。
$git add . # add 全部修改文件
$git diff --cached
4. 查看staged & unstaged(HEAD)
$git diff HEAD # staged & unstaged
$git diff # unstaged
$git diff --cached # staged
5. 保持这次修改, 全部 add 变成 staged 状态, 并 commit。
$git add .
$git commit -m "change 2"
5. 回到从前 (reset)
1.修改已commit的版本
有时候我们总会忘了什么, 比如已经提交了 commit 却发现在这个 commit 中忘了附上另一个文件. 接下来我们模拟这种情况. 上节内容中, 我们最后一个 commit 是 change 2, 我们将要添加另外一个文件, 将这个修改也 commit 进 change 2. 所以我们复制 1.py 这个文件, 改名为 2.py. 并把 2.py 变成 staged, 然后使用 --amend 将这次改变合并到之前的 change 2 中。
$git add 2.py
$git commit --amend --no-edit #"--no-edit": 不编辑, 直接合并到上一个 commit
$git log --oneline #"--oneline": 每个 commit 内容显示在一行
#输出
904e1bachange 2 # 合并过的 change 2。相比未合并前,依然是change 2,但是ID号被改变。
c6762a1change 1
13be9a7create 1.py
2.reset回到add之前
3.reset回到commit之前
HEAD 是一个指针, 指引当前的状态是在哪个 commit. 最近的一次 commit 在最右边, 我们如果要回到过去, 就是让 HEAD 回到过去并 reset 此时的 HEAD 到过去的位置。
$git reset --hard HEAD
我们可以查看 $ git reflog 里面最近做的所有 HEAD 的改动, 并选择想要挽救的 commit id。
6. 回到从前 (checkout 针对单个文件)
reset 的时候是针对整个版本库, 回到版本库的某个过去。不过如果我们只想回到某个文件的过去, 又该怎么办呢?
可以使用checkout。本节介绍改写文件checkout:
(1)回到过去
其实 checkout 最主要的用途并不是让单个文件回到过去, 我们之后会继续讲 checkout 在分支 branch 中的应用, 这一节主要讲 checkout 让文件回到过去。
我们仅仅要对 1.py 进行回到过去操作, 回到 c6762a1 change 1 这一个 commit。使用 checkout +id c6762a1 + -- + 文件目录 1.py, 我们就能将 1.py 的指针 HEAD 放在这个时刻 c6762a1:
$ git log --oneline
# 输出
904e1ba change 2
c6762a1 change 1
13be9a7 create 1.py
---------------------
$ git checkout c6762a1-- 1.py
这时 1.py 文件的内容就变成了:
a=1
(2)改写未来
我们在 1.py 加上一行内容 # I went back to change 1 然后 add 并 commit 1.py:
$ git add 1.py
$ git commit -m"back to change 1 and add comment for 1.py"
$ git log --oneline
# 输出
47f167e back to change1 and add comment for 1.py
904e1ba change 2
c6762a1 change 1
13be9a7 create 1.py
不像 reset 时那样, 我们的 change 2 并没有消失, 但是 1.py 却已经回去了过去, 并改写了未来。
7. 分支(branch)
之前我们说编辑的所有改变都是在一条主分支 master 上进行的。通常我们会把 master 当作最终的版本, 而开发新版本或者新属性的时候, 在另外一个分支上进行, 这样就能使开发和使用互不干扰了。
1. 使用branch 创建 dev 分支
我们之前的文件当中, 仅仅只有一条 master 分支,我们可以通过 --graph 来观看分支:
$ git log --oneline --graph
# 输出
* 47f167e back to change 1 and add commentfor 1.py
* 904e1ba change 2
* c6762a1 change 1
* 13be9a7 create 1.py
2. 使用checkout 创建 dev 分支
$ git branch dev # 建立dev 分支
$ git branch # 查看当前分支
# 输出
dev
* master # * 代表了当前的 HEAD 所在的分支
当我们想把 HEAD 切换去 dev 分支的时候, 我们可以用到上次说的 checkout:
$ git checkout dev
# 输出
Switched to branch 'dev'
--------------------------
$ git branch
# 输出
* dev # 这时 HEAD 已经被切换至 dev 分支
master
删除dev这个分支
$ git checkout master
$ git branch -d dev
3. 在dev 分支中修改
使用 checkout -b + 分支名, 就能直接创建和切换到新建的分支:
$ git checkout -b dev
# 输出
Switched to a new branch 'dev'
--------------------------
$ git branch
# 输出
* dev # 这时 HEAD 已经被切换至 dev 分支
master
4. 将 dev 的修改推送到master
因为当前的指针 HEAD 在 dev 分支上,所以现在对文件夹中的文件进行修改将不会影响到master 分支。们在 1.py 上加入这一行 # I was changed in dev branch, 然后再commit:
$ git commit -am"change 3 in dev" #"-am": add 所有改变并直接 commit。
不先进行add,直接输入上面的语句就行。使用-am,必须保证文件已经在管理库之中。好了, 我们的开发板 dev 已经更新好了, 我们要将 dev 中的修改推送到 master 中,大家就能使用到正式版中的新功能了。
(1)首先我们要切换到 master, 再将 dev 推送过来.
$ git checkout master # 切换至master 才能把其他分支合并过来
$ git merge dev # 将 dev merge 到 master 中
$ git log --oneline --graph
# 输出
* f9584f8 change 3 in dev
* 47f167e back to change 1 and add commentfor 1.py
* 904e1ba change 2
* c6762a1 change 1
* 13be9a7 create 1.py
可以看到并没有dev上的修改: I was changed in dev branch。
(2)要体现分支。
$ git merge --no-ff -m "keep mergeinfo" dev # 保留 merge 信息
$ git log --oneline --graph
# 输出
* c60668f keep merge info
|\
| * f9584f8 change 3 indev # 这里就能看出, 我们建立过一个分支
|/
* 47f167e back to change 1 and add commentfor 1.py
* 904e1ba change 2
* c6762a1 change 1
* 13be9a7 create 1.py
8. merge 分支冲突
想象不仅有人在做开发版 dev 的更新, 还有人在修改 master 中的一些 bug. 当我们再 merge dev 的时候, 冲突就来了. 因为 git 不知道应该怎么处理 merge 时, 在master 和 dev 的不同修改。
比如在:
master 中的 1.py 加上 # edited in master.
dev 中的 1.py 加上 # edited in dev.
在下面可以看出在 master 和 dev 中不同的 commit:
# 这是 master 的log
* 3d7796e change 4 in master # 这一条commit 和 dev 的不一样
* 47f167e back to change 1 and add comment for 1.py
* 904e1ba change 2
* c6762a1 change 1
* 13be9a7 create 1.py
-----------------------------
# 这是 dev 的log
* f7d2e3a change 3 in dev # 这一条 commit 和 master 的不一样
* 47f167e back to change 1 and add comment for 1.py
* 904e1ba change 2
* c6762a1 change 1
* 13be9a7 create 1.py
当我们想要 merge dev 到 master 的时候:
$ git branch
dev
* master
-------------------------
$ git merge dev
# 输出
Auto-merging 1.py
CONFLICT (content): Merge conflict in 1.py
Automatic merge failed; fix conflicts and then commit the result.
git 发现的我们的 1.py 在 master 和 dev 上的版本是不同的, 所以提示 merge 有冲突. 具体的冲突, git 已经帮我们标记出来, 我们打开 1.py 就能看到:
a = 1
# I went back to change 1
<<<<<<< HEAD
# edited in master
=======
# edited in dev
>>>>>>> dev
所以我们只要在 1.py 中手动合并一下两者的不同就 OK 啦. 我们将当前 HEAD (也就是master) 中的描述和 dev 中的描述合并一下。
a = 1
# I went back to change 1
# edited in master and dev
然后再 commit 现在的文件, 冲突就解决啦.
$ git commit -am "solve conflict"
再来看看 master 的 log:
$ git log --oneline --graph
# 输出
* 7810065 solve conflict
|\
| * f7d2e3a change 3 in dev 合并在一起。
* | 3d7796e change 4 in master
|/
* 47f167e back to change 1 and add comment for 1.py
* 904e1ba change 2
* c6762a1 change 1
* 13be9a7 create 1.py
9. rebase 分支冲突
什么是rebase?是一种更高级的合并方式,同样是合并rebase 的做法和 merge 不一样。
和 merge 不一样。 merge 在合并的时候创建了一个新的commit. 这一点不同, 使得在共享分支中使用 rebase 变得危险。如果是共享分支的历史被改写。别人之前共享内容的 commit 就被你的 rebase 修改掉了。
10. 临时修改 (stash)
不想把要改的程序和自己改进代码的部分一起commit。这时 stash 就是我的救星了. 用 stash 能先将我的那改进的部分放在一边分隔开来. 再另外单独处理老板的任务。
1.暂存修改
$ git stash
将这些改变暂时放在一边。
2.做其它任务
建立另一个 branch 用来完成老板的任务。
3.恢复暂存
可以通过 pop 来提取这个并继续工作了。
11. Github 在线代码管理
1. 建立Github版本库
Git:本地管理库;Github:在线管理库。添加你的一个 online 版本库 repository时,
版本库的名称不一定和本地的是一样的。
将Github上的管理库链接上你的本地版本库。
2. 连接本地版本库
打开bash,保证文件在当前的路径下,然后输入…orcreate a new repository on the command line中的最后两行。
3. 推送修改