本文不是 git 的入门文章,介绍也不是很详细,只是罗列在实际开发过程中的一些具体指令使用,由于水平有限,如果讲解存在偏差,不吝赐教。
PS:以下例子基于window 的 git 命令行
commit 信息更正
一切准备就绪,拉好本地分支,你开心的撸着代码了,完成了部分任务,准备来一次commit。
$ git commit -m "完成 task1"
[master f5d2634] 完成 task1
1 file changed, 2 insertions(+), 1 deletion(-)
结果呢,boss说的要使用英文的commit信息,不要搞些中文在上面。这可怎么办呢?首先我们先确定一下当前的提交信息,这里就需要使用到 git log
的指令了,为了显示简短的消息,可以加上 --oneline
。
$ git log --oneline
f5d2634 完成 task1
b7de6d2 master first
嗯,看来已经是提交成功了。这个时候就想修改commit的信息怎么操作呢?这里就可以使用 $ git commit --amend -m
的指令了。
$ git commit --amend -m "finish task1"
[master 470c5f6] finish task1
Date: Wed Mar 22 16:47:13 2017 +0800
1 file changed, 2 insertions(+), 1 deletion(-)
接下来再看看提交历史,你会发现,已经修改成功了,nice,不会被boss说了。
$ git log --oneline
470c5f6 finish task1
b7de6d2 master first
合并分支解决冲突
比如说我们现在有一个新的分支(dev),然后修改了一些文件,又新建了一个文件:
$ git status
On branch dev
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: code.txt
Untracked files:
(use "git add <file>..." to include in what will be committed)
code2.txt
首先咱们先 commit code.text
的修改,然后将 code2.txt
加入列表中。然后现在 dev
分支的 log 就是这样的:
$ git log --oneline
851eec5 update code.txt
b2e434c new branch new file
470c5f6 finish task1
b7de6d2 master first
要合并分支,那我就不管啦,直接 merge
了吧,结果这里就给你报错了,意思是说你本地修改的文件在合并的时候将会被复写,你必须得提交你的修改或者保存它们。其实git
都提示你得灰常清楚了,那么接下来应该怎么处理呢?你不能没有完成就 commit
一次吧?这时候就可以暂存本地的修改( stash
),合并之后再恢复本地的修改。
$ git merge dev
Updating 470c5f6..851eec5
error: Your local changes to the following files would be overwritten by merge:
code.txt
Please commit your changes or stash them before you merge.
Aborting
现在 master
分支最新的代码是这个样子的,增加了一行"hello kugou" ,接下来先保存这个修改,然后合并 dev
分支。
$ git diff
diff --git a/code.txt b/code.txt
index 8da6944..dc47a8b 100644
--- a/code.txt
+++ b/code.txt
@@ -1,2 +1,3 @@
hello first!
+hello kugou
good job
\ No newline at end of file
stash 的使用
$ git stash
Saved working directory and index state WIP on master: 470c5f6 finish task1
HEAD is now at 470c5f6 finish task1
就这样,我们本地的修改就被保存起来了, 你还可以保存几个呢,通过 git stash list
可以查看相关的列表。接着就看是合并分支咯。接着再看看 master
分支现在的情况呢。
$ git stash
Saved working directory and index state WIP on master: 470c5f6 finish task1
HEAD is now at 470c5f6 finish task1
那接下来就可以开始分支合并了。
$ git merge dev
Updating 470c5f6..851eec5
Fast-forward
code.txt | 4 +++-
code2.txt | 1 +
2 files changed, 4 insertions(+), 1 deletion(-)
create mode 100644 code2.txt
因为我们 master
分支的 code.txt
的修改被暂存了,所以现在直接就快速合并了,并没有提示合并冲突,接下来,我们要取出刚刚保存的提交。
$ git stash pop
Auto-merging code.txt
CONFLICT (content): Merge conflict in code.txt
这里就冲突咯,接下来就看看怎么解决冲突。
hello first!
<<<<<<< Updated upstream
good dev
good job
=======
hello kugou
good job
>>>>>>> Stashed changes
其实就是 good dev
和 hello kugou
冲突了,我们这里选择都保留下来吧。
hello first!
good dev
hello kugou
good job
修改之后,使用指令 $ git add -u
就是--update
的简化。接着 commit
就好了。
$ git commit -m"fix conflict"
[master cf4876a] fix conflict
1 file changed, 5 insertions(+)
代码回到过去
在 commit
这里,如果你没有 添加对应的信息,直接使用 git commit
会跳到一个信息输入的奇怪页面。说这个之前,先说说代码回滚吧。就比如说刚刚我们的提交,其实也不能直接 commit
的,因为我们就是因为 master
分支的工作没有完成才去 stash
的啊,肯定应该做完了才一起提交的呢,怎么办呢?这里当然就是代码回滚咯。
$ git log --oneline
cf4876a fix conflict
851eec5 update code.txt
b2e434c new branch new file
470c5f6 finish task1
b7de6d2 master first
$ git reset
这个指令可以用于代码回滚,代码回滚肯定要先明确我们需要回滚到具体哪个位置咯。还有就是是否要舍弃掉对应提交的内容。这里常用的有 --hard
,--soft
两种, hard
就是回滚后,本地对应的文件全部丢失,不会保存, soft
就是你写的东西全部都在。这里当然应该使用 soft
模式,因为刚刚合并后的结果是有效的嘛,应该保留。
$ git reset --soft HEAD^
$ git log --oneline
851eec5 update code.txt
b2e434c new branch new file
470c5f6 finish task1
b7de6d2 master first
我们的确回到过去了,那么看看对应的内容呢。
hello first!
good dev
hello kugou
good job
肯定和之前是一样的啦。接着在最后面加入一行 master finish task2
$ git diff
diff --git a/code.txt b/code.txt
index 8062f1a..ab3c954 100644
--- a/code.txt
+++ b/code.txt
@@ -2,3 +2,5 @@ hello first!
good dev
hello kugou
good job
+
+master finish task2
\ No newline at end of file
commit 的另一种提交方式
接着继续 commit
,这次后面 不加 -m
,看看会怎么样。以前我是懵逼的,这个是撒玩意儿,后面终于会玩儿了,这里其实就是要你输入提交信息。操作步骤很简单。
第一步,按一下 i
(insert) 插入信息,下面会有 -- INSERT --
的标志,第二步,输入提交信息巴拉巴拉。然后最后这个输入状态如何退出呢?
第三步,退出输入模式,按下 Esc
接着输入 :wq
(可以记为 write over quit,看清楚啦,是三个字符),回车,这样就退出了编辑模式了。
$ git log --oneline
2a1db91 task2 done!
851eec5 update code.txt
b2e434c new branch new file
470c5f6 finish task1
b7de6d2 master first
代码回到未来
可以看到,相关 commit
已经搞定了。刚刚说了版本回退,那么回退之后又怎么回来呢?因为回退之后你在 log 里面根本看不到回退前的那个commit信息了。这时候需要使用 reflog
的的指令了。
$ git reflog
2a1db91 HEAD@{0}: commit: task2 done!
851eec5 HEAD@{1}: reset: moving to HEAD^
80aed65 HEAD@{2}: commit: fix conflict
851eec5 HEAD@{3}: reset: moving to HEAD^
cf4876a HEAD@{4}: reset: moving to cf4876a
851eec5 HEAD@{5}: reset: moving to HEAD^
cf4876a HEAD@{6}: commit: fix conflict
851eec5 HEAD@{7}: merge dev: Fast-forward
你可以找到你的相关操作,然后还是使用 reset
,就可以回去了。举个例子,例如你现在已经回退到了 cf4876a HEAD@{6}: commit: fix conflict
这里,然后想回到 2a1db91 HEAD@{0}: commit: task2 done!
这里。
$ git reset --hard cf4876a
HEAD is now at cf4876a fix conflict
$ git reset --hard 2a1db91
HEAD is now at 2a1db91 task2 done!
就这样,就完成了一次完美的时光穿梭了。
多个远程仓库关联
你以为只可以关联一个远程仓库?根本不是这样的,你完全可以添加很多仓库的。这里就涉及到 remote
相关的指令。
添加远程仓库:
$ git remote add origin git@github.com:lovejjfg/screenshort.git
$ git remote -v
origin git@github.com:lovejjfg/screenshort.git (fetch)
origin git@github.com:lovejjfg/screenshort.git (push)
这里,你可以看到为什么那个远程仓库是叫做 origin
的了吧。
这个是可以自己指定的,但是约定俗成的就是这么叫,另外,你如果需要添加新的一个,例如这样:
$ git remote add upstream git@github.com:lovejjfg/Circle.git
$ git remote -v
origin git@github.com:lovejjfg/screenshort.git (fetch)
origin git@github.com:lovejjfg/screenshort.git (push)
upstream git@github.com:lovejjfg/Circle.git (fetch)
upstream git@github.com:lovejjfg/Circle.git (push)
这个 upstream
也是约定俗成的命名,你也完全可以随性而为,不碍事儿。接着在提交 push
代码的时候, 理论上你应该指定对应的远程仓库了。 但是你是不是觉得你 push pull
的时候根本就没有指定对应的远程分支,这个又是怎么做到的?
那是因为你已经设置了默认的,也很简单:
//before
$ git push
fatal: No configured push destination.
Either specify the URL from the command-line or configure a remote repository using
git remote add <name> <url>
and then push using the remote name
git push <name>
$ git push --set-upstream test1 master
Branch master set up to track remote branch master from test1.
Everything up-to-date
//after
$ git push
Everything up-to-date
rebase vs merge
一句话总结这两者的话,那就是 rebase
会依次比较每一个 commit
,最终不会产生新的 commit
,是在一条线上的,而 merge
是比较两个分支的根节点以及各自最后的一个 commit
,最后会产生一个新的 commit
,会产生多条分支线。
必须强调,不要去 rebase
已经公开的提交,不然你会给小伙伴带来很多灾难。
这句话如何理解,接着上面的例子,我们先在 master
的分支基础上拉出新的分支 dev2
,我们在 master
分支上提交一个新的 commit
,然后以此为基础,更新合并代码到新的 dev2
分支。到这里是完全没问题的,不会有任何的冲突。
$ git commit -m "commit in master"
[master 0b4f692] commit in master
1 file changed, 1 insertion(+)
查看 master
分支的最新 log :
$ git log --oneline
0b4f692 commit in master
2a1db91 task2 done!
851eec5 update code.txt
b2e434c new branch new file
470c5f6 finish task1
b7de6d2 master first
合并 master
分支到 dev2
分支:
$ git rebase master dev2
First, rewinding head to replay your work on top of it...
Fast-forwarded dev2 to master.
接着我们切到 dev
分支完成一个新的 commit
,这个 commit
确保会和刚刚 master
分支的 commit
冲突,我也是蛮拼的哟,故意制造冲突来着。
$ git rebase dev master
First, rewinding head to replay your work on top of it...
Applying: commit in master
Using index info to reconstruct a base tree...
M code.txt
Falling back to patching base and 3-way merge...
Auto-merging code.txt
CONFLICT (content): Merge conflict in code.txt
error: Failed to merge in the changes.
Patch failed at 0001 commit in master
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
果然,我的 dev
分支和 master
分支完美的冲突了,而且我使用的是 rebase
的模式。按照这个提示信息,我修复了冲突,执行 git rebse --continue
的指令。
$ git add -u
$ git rebase --continue
Applying: commit in master
接着我们再来看看现在 master
的 log :
$ git log --oneline
0916463 commit in master
17b11f8 dev commit
2a1db91 task2 done!
851eec5 update code.txt
b2e434c new branch new file
470c5f6 finish task1
b7de6d2 master first
好像是没有什么问题哈, dev commit
合并过来了,冲突完美解决,并且没有产生新的 commit
。 但是,往上看你会发现,commit in master
这条 commit
和之前的 编号不一样了。不过无所谓,反正冲突我合并好了。
//before
0b4f692 commit in master
//after
0916463 commit in master
这里就引出了刚刚那个建议,不要 rebase
已经公开的 commit
,因为这个 commit in master
已经公开到 dev2
分支了,这个就可以理解为另外一个同事那儿的最新代码,他没做任何修改,应你的要求,同步更新一下 master
分支的代码:
$ git rebase master dev2
First, rewinding head to replay your work on top of it...
Applying: commit in master
Using index info to reconstruct a base tree...
M code.txt
Falling back to patching base and 3-way merge...
Auto-merging code.txt
CONFLICT (content): Merge conflict in code.txt
error: Failed to merge in the changes.
Patch failed at 0001 commit in master
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
$ git merge --abort
$ git merge master
Auto-merging code.txt
CONFLICT (content): Merge conflict in code.txt
Automatic merge failed; fix conflicts and then commit the result.
你就说你坑不坑,人家就是同步更新下代码啊,啥都没有做,你说他是不是会一脸的懵逼,经验不足的估计吓尿了,我改了撒怎么就冲突了呢?!这里这个兄弟不管是用 rebase
或者 merge
的方式都是会冲突的,因为它的分支的节点已经被修改了。
$ git log --oneline
0b4f692 commit in master//这个还是before的
2a1db91 task2 done!
851eec5 update code.txt
b2e434c new branch new file
470c5f6 finish task1
b7de6d2 master first
那么这里,我们坐着时光机,代码回滚到 rebase
之前, master
分支的 0b4f692 commit in master
,既然知道刚刚的 rebase dev
的方式会坑人,这次我们就使用 merge
来试一下。
$ git checkout master
Switched to branch 'master'
$ git reset --hard 0b4f692
HEAD is now at 0b4f692 commit in master
$ git merge dev master
Auto-merging code.txt
CONFLICT (content): Merge conflict in code.txt
Automatic merge failed; fix conflicts and then commit the result.
有冲突是必须得,还是老样子全部保留,然后解决冲突,产生一个新的commit。
$ git add -u
$ git commit "fix conflicts"
fatal: cannot do a partial commit during a merge.
$ git commit -m"fix conflicts"
[master 1ba333a] fix conflicts
接着,那位同事要去合并代码的话,就不会产生冲突啦。
$ git rebase master dev2
First, rewinding head to replay your work on top of it...
Fast-forwarded dev2 to master.
这样,是不是又知道了一种产生冲突的情况啦?所以一定要记住强调的:**千万不要 rebase
已经公开的commit
**.以后要是遇到代码冲突了,不要着急,先明确是什么情况造成的。这种情况的话,你必须的说,这个锅,我不背。当然自己也要做到不要给同事挖坑啦,不然会被鄙视的。
最后,希望开发的时候养成勤拉分支,实时合并,及时删除合并后的分支的好习惯,这几个步骤都应该有,尤其是最后一个。如果公司还没有使用 git
,你可以去 github
练练手,毕竟现在 git
是趋势,多学没坏处的。
到这里,git
在开发中使用就讲的差不多了,现在有很多 git
的可视化工具了,如果是做 Android
开发的话, Android Studio
的版本控制功能已经很强大了,所以你大可不必记住上面的这些繁琐的命令行操作,你开心的话,想怎么玩就怎么玩。
git 资料
版权印为您的作品印上版权75500426