近日,美国证券交易委员会指控特斯拉公司首席执行官埃隆·马斯克,他之前发表推特称筹集到了对该公司私有化所需的资金,属于误导投资者。同时要求法官下令,禁止马斯克在被判有罪时担任上市公司的高管或董事。消息传出后,特斯拉股价在盘后交易中一度暴跌13%。
本篇来自 剑走偏锋雨 的投稿,分享了自己使用Git命令行的经验,希望对大家有所帮助。 剑走偏锋雨 的博客地址: https://www.jianshu.com/u/4d1476815da2
之前不太重视Git的命令行操作,直到去了前些天一直炒的非常火的某某某某公司,呆了几天帮忙解BUG,发现项目不用AndroidStudio的图形化界面操作版本控制,一时间傻眼......想到应该也有小伙伴和我一样,所以就有了这篇文章,希望通过阅读这篇文章大家都能学会Git命令行操作。 讲述方式非面面俱到。喜欢后者的,可以买本书细细品。 Git 是分布式版本控制系统,客户端保留完整的代码仓库,就算搭载远程仓库的服务器出了故障,客户端的数据也可以用来恢复服务器数据。 Git 操作几乎都在本地完成,速度快,效率高,且不受网络限制。
Git以校验和的方式检测数据的完整性。采用的校验和机制叫做SHA-1散列。SHA-1散列由40个十六进制字符(0-9和a-f)所组成的字符串,这些字符串是根据文件内容和Git的目录结构计算所得。在Git中commit id 就是校验和,如下: $git log commit 555af4a0aadb6c64a73acfd94c8fe89af5604083 Author: Fu Kaiqiang <>@xxx.com> Date: Mon Sep 17 10:45:24 2018 +0800 first commit
$ git config --global user.name 'Your Name' $ git config --global user.email 'email@example.com'
说明:--global 表示你这台机器上所有的Git仓库都会使用这个配置 $git config --list user.email=fukaiqiang@xxx.com user.name=Fu Kaiqiang alias.st=status alias.co=checkout alias.dt=difftool -t meld -y color.ui=auto core.repositoryformatversion=0 core.filemode=true core.bare=false core.logallrefupdates=true remote.origin.url=git@github.com:OnlyYouMyLove/TestGit.git remote.origin.fetch=+refs/heads/*:refs/remotes/origin/* branch.master.remote=origin branch.master.merge=refs/heads/master
不同操作系统推荐使用不同编辑器,Linux推荐使用Vim,Window可使用NotePad++ 说明:vim 可以创建并打开TestGit.md文件。不是重点,不详述,感兴趣,可找小度或小谷。
说明:初始化仓库,生成 .git 文件即可 $git clone git@github.com:OnlyYouMyLove/TestGit.git Cloning into 'TestGit'... remote: Counting objects: 6, done. remote: Compressing objects: 100% (4/4), done. remote: Total 6 (delta 0), reused 6 (delta 0), pack-reused 0 Receiving objects: 100% (6/6), 4.76 KiB | 0 bytes/s, done. Checking connectivity... done.
说明: ① 初始化仓库和克隆远程仓库是创建本地仓库两种方式,根据场景不同,选择不同的创建方式。 ② 本地仓库初始化以后,还需要和远程仓库进行关联,远程仓库部分讲。
$ git commit -m 'wrote a readme file'
说明: -m后添加此次提交的说明 $git commit -a -m 'skip stage to commit' On branch master Your branch is ahead of 'origin/master' by 3 commits. (use 'git push' to publish your local commits) nothing to commit, working directory clean
说明:-a 参数可以自动暂存文件,然后执行提交。 $git log commit 462a66ec484df873bc5638fbd53523408ecfed43 Merge: 34a6e3f 44d733a Author: Fu Kaiqiang <>@xxx.com> Date: Fri Sep 14 10:28:10 2018 +0800
Merge remote-tracking branch 'origin/master'
commit 34a6e3f6dba34d4870da3f9a25ab653a33ae2a49 Author: Fu Kaiqiang <>@xxx.com> Date: Fri Sep 14 10:21:42 2018 +0800
Today is Friday!
$git log --pretty=oneline 462a66ec484df873bc5638fbd53523408ecfed43 Merge remote-tracking branch 'origin/master' 34a6e3f6dba34d4870da3f9a25ab653a33ae2a49 Today is Friday!
$git status On branch master Your branch is up-to-date with 'origin/master'. Changes to be committed: (use 'git reset HEAD ...' to unstage)
modified: TestGit.md
Untracked files: (use 'git add ...' to include in what will be committed)
.TestGit.md.swo .TestGit.md.swp
说明: ① Changes to be committed:已暂存、可提交文件 ② Untracked files:未暂存文件 $git diff diff --git a/TestGit.md b/TestGit.md index 1de64fb..89aaf06 100644 --- a/TestGit.md +++ b/TestGit.md @@ -1,2 +1,3 @@ Hello World Hello Chuizi +Hello Branch
$git diff --staged diff --git a/TestGit.md b/TestGit.md index bc8bff8..2d675a0 100644 --- a/TestGit.md +++ b/TestGit.md @@ -2,3 +2,4 @@ Hello World Hello Chuizi Hello HUAWEI Hello XIAOMI +Hello MEIZU
说明:文件一旦暂存,通过git diff命令就不能查询差异,必须添加--staged参数,或--cached参数。
文件内容: 1 Hello World 2 Test Git 3 Hello ChuiZi 4 Hello JianGuo 5 hahahahah 6 fdafafafafaf 7 fajfkajfakdjfk 8 eight line 9 nine line 10 tenth line
$git log commit c836aafa9e33890aebd9b53c816e3180ccd4ddb7 Author: Fu Kaiqiang <>@smartisan.com> Date: Fri Sep 14 17:01:59 2018 +0800
add tenth line
commit b1a66343568506fdb9960ebd3d896fd798ac61a4 Author: Fu Kaiqiang <>@smartisan.com> Date: Fri Sep 14 17:01:09 2018 +0800
add ninth line
$git reset --hard HEAD^ HEAD is now at b1a6634 add ninth line
说明:以上这句命令行代码是核心代码 $git log commit b1a66343568506fdb9960ebd3d896fd798ac61a4 Author: Fu Kaiqiang <>@smartisan.com> Date: Fri Sep 14 17:01:09 2018 +0800
add ninth line
文件内容: 1 Hello World 2 Test Git 3 Hello ChuiZi 4 Hello JianGuo 5 hahahahah 6 fdafafafafaf 7 fajfkajfakdjfk 8 eight line 9 nine line
$git reflog c836aaf HEAD@{0}: reset: moving to c836a b1a6634 HEAD@{1}: reset: moving to HEAD^ c836aaf HEAD@{2}: commit: add tenth line b1a6634 HEAD@{3}: commit: add ninth line
说明:内容包括 ① commit id ② 执行的命令 ③ 描述 $git reset --hard c836a HEAD is now at c836aaf add tenth line
说明: ① 找到commit id,就可以回退到指定版本。 ② commit id 不用填全部,填写部分,git 自动查找。 修改TestGit.md内容为: Hello World
再修改TestGit.md内容为: Hello World Hello Chuizi
$git commit -m 'Two line' [master 7dd7cf8] Two line 1 file changed, 1 insertion(+), 11 deletions(-)
$git status On branch master Your branch is ahead of 'origin/master' by 1 commit. (use 'git push' to publish your local commits) Changes not staged for commit: (use 'git add ...' to update what will be committed) (use 'git checkout -- ...' to discard changes in working directory)
modified: TestGit.md
no changes added to commit (use 'git add' and/or 'git commit -a')
说明:这时发现还有未添加到暂存区的内容。 $git diff HEAD -- TestGit.md diff --git a/TestGit.md b/TestGit.md index 35eab83..1de64fb 100644 --- a/TestGit.md +++ b/TestGit.md @@ -1,2 +1,2 @@ Hello World - +Hello Chuizi
说明: ① 发现第二次修改没有提交到暂存区,导致 git commit 提交的只是第一次的修改。 ② git diff 命令是核心代码。 ③ 这个时候再执行一遍add和commit操作即可。
fukqdembp:TestGit fukq$ git status On branch master Your branch is up to date with 'origin/master'.
Changes not staged for commit: (use 'git add ...' to update what will be committed) (use 'git checkout -- ...' to discard changes in working directory)
modified: TestGit.md
no changes added to commit (use 'git add' and/or 'git commit -a')
说明:discard changes - 放弃修改:的提示为 'git checkout -- ...' fukqdembp:TestGit fukq$ git checkout -- TestGit.md fukqdembp:TestGit fukq$ git status On branch master Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
fukqdembp:TestGit fukq$ git add TestGit.md fukqdembp:TestGit fukq$ git status On branch master Your branch is up to date with 'origin/master'.
Changes to be committed: (use 'git reset HEAD ...' to unstage)
modified: TestGit.md
说明:to unstage - 不暂存的提示为 'git reset HEAD ...' fukqdembp:TestGit fukq$ git reset HEAD TestGit.md Unstaged changes after reset: M TestGit.md fukqdembp:TestGit fukq$ git status On branch master Your branch is up to date with 'origin/master'.
Changes not staged for commit: (use 'git add ...' to update what will be committed) (use 'git checkout -- ...' to discard changes in working directory)
modified: TestGit.md
no changes added to commit (use 'git add' and/or 'git commit -a') fukqdembp:TestGit fukq$
fukqdembp:TestGit fukq$ ls LICENSE README.md TestGit.md TestGit.txt
fukqdembp:TestGit fukq$ rm TestGit.txt
fukqdembp:TestGit fukq$ git status On branch master Your branch is ahead of 'origin/master' by 1 commit. (use 'git push' to publish your local commits)
Changes not staged for commit: (use 'git add/rm ...' to update what will be committed) (use 'git checkout -- ...' to discard changes in working directory)
deleted: TestGit.txt
no changes added to commit (use 'git add' and/or 'git commit -a')
fukqdembp:TestGit fukq$ git rm TestGit.txt rm 'TestGit.txt'
fukqdembp:TestGit fukq$ git commit TestGit.txt -m '删除TestGit.txt文件' [master 7075b5f] 删除TestGit.txt文件 1 file changed, 1 deletion(-) delete mode 100644 TestGit.txt fukqdembp:TestGit fukq$ git status On branch master Your branch is ahead of 'origin/master' by 2 commits. (use 'git push' to publish your local commits)
nothing to commit, working tree clean
fukqdembp:TestGit fukq$ ls LICENSE README.md TestGit.md
说明: ① rm TestGit.txt ② git rm TestGit.txt ③ git commit TestGit.txt -m '删除TestGit.txt文件' fukqdembp:TestGit fukq$ rm TestGit.txt
fukqdembp:TestGit fukq$ git status On branch master Your branch is ahead of 'origin/master' by 3 commits. (use 'git push' to publish your local commits)
Changes not staged for commit: (use 'git add/rm ...' to update what will be committed) (use 'git checkout -- ...' to discard changes in working directory)
deleted: TestGit.txt
no changes added to commit (use 'git add' and/or 'git commit -a')
fukqdembp:TestGit fukq$ git checkout -- TestGit.txt
fukqdembp:TestGit fukq$ ls LICENSE README.md TestGit.md TestGit.txt
说明:$ git checkout --TestGit.txt 既可以对未添加到暂存区的修改操作进行恢复,也可以对已删除的文件进行恢复。
$git remote add origin git@github.com:OnlyYouMyLove/TestGit.git
说明: ① 组成 -- git remote add + 自定义仓库命名 + 仓库ssh地址 ② git init 初始化仓库以后需要进行 git remote操作,实现本地和远程仓库的绑定,这才能算是完整创建本地仓库。 $git push -u origin master
说明: ① -u 参数的添加可以让本地仓库和远程仓库进行绑定 ② 如果报以下错,意思是缺少权限--本地生成ssh,添加到github的ssh列表即可: $git push -u origin master Warning: Permanently added the RSA host key for IP address 'xxx.xx.xxx.xxx' to the list of known hosts. Permission denied (publickey). fatal: Could not read from remote repository.
Please make sure you have the correct access rights and the repository exists.
③ 如果报以下错,输入yes即可: The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established. RSA key fingerprint is xx.xx.xx.xx.xx. Are you sure you want to continue connecting (yes/no)?
Warning: Permanently added 'github.com' (RSA) to the list of known hosts.
说明:警告只会出现一次。 ④ 查看ssh key (linux系统下,其他系统大同小异): 说明: ① git clone 自动命名远程仓库服务器名字为origin,并拉取全部数据,然后在本地创建指向服务器上master分支的指针,并命名为origin/master,它就叫远程分支,接着git会创建本地master分支。 ② fetch 这条命令会从服务器取得所有本地尚未包含的数据,然后更新远程分支。注意:只是更新远程分支,本地master分支并未改变,需要合并才行。 ③ origin 与 master分支名称一样,只是默认名称,没有什么特殊含义,都可以自定义。 $git merge origin/master Updating ce50a7f..253d867 Fast-forward TestGit.md | 1 + 1 file changed, 1 insertion(+)
说明:只要经过fetch和merge才能把远程仓库的数据同步给本地仓库。 $git pull origin master From github.com:OnlyYouMyLove/TestGit * branch master -> FETCH_HEAD Updating ce50a7f..5ec3b49 Fast-forward .test.swp | Bin 0 -> 4096 bytes TestGit.md | 3 +++ 2 files changed, 3 insertions(+) create mode 100644 .test.swp
说明:pull 命令直接拉取数据到远程分支并且合并数据到master分支。
$git checkout -b dev Switched to a new branch 'dev'
说明:-b 参数 意思是 创建并切换分支,相当于下面两条命令 $ git branch dev 创建分支 $ git checkout dev 切换分支 Switched to branch 'dev'
$git branch * master
$git checkout -b dev Switched to a new branch 'dev'
$git status On branch dev nothing to commit, working directory clean
$git branch * dev master
说明:核心命令--git branch,并用 * 指向当前所在分支 $git branch * dev master
$vim TestGit.md
$git checkout master M TestGit.md Switched to branch 'master' Your branch is up-to-date with 'origin/master'.
$git branch dev * master
$git merge dev Already up-to-date.
说明:操作步骤如下 ① 在dev分支上编辑TestGit ② 切换到master分支 ③ 合并dev到当前分支master $git branch -d dev Deleted branch dev (was 555af4a).
创建并切换到dev分支 $git checkout -b dev M TestGit.md Switched to a new branch 'dev'
编辑TestGit.md文件 $vim TestGit.md
编辑以后: Hello World Hello Chuizi Hello XIAOMI
提交 $git commit TestGit.md -m 'dev commit file' [dev 6a31928] dev commit file 1 file changed, 1 insertion(+)
切换到master分支 $git checkout master Switched to branch 'master' Your branch is up-to-date with 'origin/master'.
编辑TestGit.md文件 $vim TestGit.md
编辑以后: Hello World Hello Chuizi Hello HUAWEI
提交 $git commit TestGit.md -m 'master commit file' [master b62ed88] master commit file 1 file changed, 1 insertion(+)
合并 $git merge dev Auto-merging TestGit.md CONFLICT (content): Merge conflict in TestGit.md Automatic merge failed; fix conflicts and then commit the result.
编辑TestGit.md文件 $vim TestGit.md
Hello World Hello Chuizi <> Hello HUAWEI ======= Hello XIAOMI >>>>>>> dev
说明:=======上面是HEAD(master)版本 >>>>>>>上面是dev版本 解决冲突--进行编辑: Hello World Hello Chuizi Hello HUAWEI Hello XIAOMI
提交 $git commit TestGit.md fatal: cannot do a partial commit during a merge.
说明:出现冲突以后,修改文件解决冲突后,不能提交部分文件 上面的commit修改为提交所有文件即可 $git commit -m 'merge files' [master 39791ca] merge files
$git log --graph --pretty=oneline --abbrev-commit * 39791ca merge files |\ | * 6a31928 dev commit file * | b62ed88 master commit file |/ * 555af4a first commit
从合并图可以看出来是dev和master的合并: ① 555af4a first commit :首次的提交 ② 6a31928 dev commit file b62ed88 master commit file :dev先提交master后提交 ③ 39791ca merge files :解决冲突后合并提交
$git log --graph --pretty=oneline --abbrev-commit * 39791ca merge files |\ | * 6a31928 dev commit file * | b62ed88 master commit file |/ * 555af4a first commit
上面是分支冲突后的合并图log,你会发现非常混乱,两个commit一个merge,并不在一条直线上。而这仅仅是一次冲突的合并。合并越多,历史log越乱!一个项目的合并次数可能达到成百上千,如果历史合并log非常混乱,会对阅读造成非常大的障碍,影响工作效率,那能否左边的图是一条直线而非菱形呢?答案是肯定的,当本地出现冲突的时候使用变基--rebase来替代合并--merge。 那为什么merge后会产生菱形历史?这就要从其实质说起 : dev 分支、master 分支、两者的共同祖先一起合并生成新的快照,我们称这种提交叫做“合并提交',而合并提交并非只有一个父提交,这里有两个dev和master。所以合并历史图出现了菱形。
复现冲突场景:dev和master分别修改了同一个文件的相同地方,并且执行了暂存和提交,然后我们这里执行rebase操作: $git checkout dev Switched to branch 'dev'
$git rebase master First, rewinding head to replay your work on top of it... Applying: dev commit Using index info to reconstruct a base tree... M TestGit.md Falling back to patching base and 3-way merge... Auto-merging TestGit.md CONFLICT (content): Merge conflict in TestGit.md error: Failed to merge in the changes. Patch failed at 0001 dev commit 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'.
说明:rebase失败,让先解决冲突,然后用 'git rebase --continue'
$git rebase --continue Applying: dev commit
$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 Updating 6321248..ce50a7f Fast-forward TestGit.md | 1 + 1 file changed, 1 insertion(+)
$git log --graph --pretty=oneline --abbrev-commit * ce50a7f dev commit * 6321248 master commit * 555af4a first commit
为什么变基可以实现一条线的历史提交效果呢?那我们也看看其原理:首先找到两个要整合的分支(你当前所在的分支和要整合到的分支)的共同祖先,然后取得当前所在分支的每次提交引入的更改(diff),并把这些更改保存为临时文件,这之后将当前分支重置为要整合到的分支,最后在该分支上依次引入之前保存的每个更改。说白了就是记录dev的更改并保存,然后把保存的更改依次引入master分支,这样相当于所有的更改都在master上。merge和rebase的结果相同,但是提交历史完全不同。 你说还不明白,还是没有看出区别!但是你仔细想想其实区别很大:变基实际上是抛弃了dev的已有的提交,随后创建了新的对应提交,内容相似,但是却是不同的提交。
|