Git 指令的相关使用场景

本文不是 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 devhello 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 ,看看会怎么样。以前我是懵逼的,这个是撒玩意儿,后面终于会玩儿了,这里其实就是要你输入提交信息。操作步骤很简单。

commit.png

第一步,按一下 i (insert) 插入信息,下面会有 -- INSERT -- 的标志,第二步,输入提交信息巴拉巴拉。然后最后这个输入状态如何退出呢?
insert.png

第三步,退出输入模式,按下 Esc 接着输入 :wq (可以记为 write over quit,看清楚啦,是三个字符),回车,这样就退出了编辑模式了。
write over.png

$ 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 资料

廖雪峰的git教程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值