300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > 版本控制工具——Git常用操作(下)

版本控制工具——Git常用操作(下)

时间:2021-09-24 04:29:08

相关推荐

版本控制工具——Git常用操作(下)

本文由云+社区发表

作者:工程师小熊

摘要:上一集我们一起入门学习了git的基本概念和git常用的操作,包括提交和同步代码、使用分支、出现代码冲突的解决办法、紧急保存现场和恢复现场的操作。学会以后已经足够我们使用Git参加协作开发了,但是在开发的过程中难免会出错,本文主要介绍版本控制的过程中出错了的场景,以及Git开发的一些技巧,让我们用的更流畅。

上集回顾:

Git的基本概念一个人使用Git时的代码版本控制--(提交、拉代码、分支操作)多人合作时的代码版本控制--(合并冲突、暂存代码)

本文核心:

后悔药-各种后悔操作(撤消commit,回滚,回退远程仓库等)哎呀,提交的时候漏了文件tag操作git忽略不想提交的文件

后悔药

撤消当前commit

如果你发现刚刚的操作一不小心commit了,所幸你还没有推送到远程仓库,你可以用reset命令来撤消你的这次提交。

reset命令的作用:重置HEAD(当前分支的版本顶端)到另外一个commit。

我们的撤消当前提交的时候往往不希望我们此次提交的代码发生任何丢失,只是撤消掉commit的操作,以便我们继续修改文件。如果我们是想直接不要了这次commit的全部内容的任何修改我们将在下一小节讨论。

来,我们先说一句蠢话来diss老板

$ touch to_boss.txt$ echo 'my boss is a bad guy!' > to_boss.txt$ git add to_boss.txt$ git statusOn branch masterYour branch is up to date with 'origin/master'.Changes to be committed:(use "git reset HEAD <file>..." to unstage)new file: to_boss.txt$ git commit -m "[+]骂了我的boss"[master 3d113a7] [+]骂了我的boss1 file changed, 1 insertion(+)create mode 100644 to_boss.txt

创建to_boss.txt文件,并向其写入了my boss is a bad guy!add然后status查看新文件已经加入跟踪commit提交了这次的修改

好了,刚刚我们“不小心”diss了我们的老板,要是被发现就完了,所幸还没有push,要快点撤消这些提交,再换成一些好话才行。

我们使用以下命令:

$ git reset --soft head^$ git statusOn branch masterYour branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.(use "git pull" to update your local branch)Changes to be committed:(use "git reset HEAD <file>..." to unstage)new file: to_boss.txt$ cat to_boss.txtmy boss is a bad guy!$ echo 'my boss is a good boy!'my boss is a good boy!$ echo 'my boss is a good boy!' > to_boss.txt$ cat to_boss.txtmy boss is a good boy!$ git add to_boss.txt$ git statusOn branch masterYour branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.(use "git pull" to update your local branch)Changes to be committed:(use "git reset HEAD <file>..." to unstage)new file: to_boss.txt$ git commit -m "[*]夸了我的boss"[master 8be46aa] [*]夸了我的boss1 file changed, 1 insertion(+)create mode 100644 to_boss.txt

git reset --soft head^撤消了本次提交,将工作区恢复到了提交前但是已经add的状态将to_boss.txt的内容改成了my boss is a good boy!add然后commit提交

好了,有惊无险,这就是撤消commit的操作。另一种情况是如果你想撤消commit的时候支持舍弃这次全部的修改就把git reset --soft head^改成git reset --hard head^,这样你本地修改就彻底丢掉了(慎用),如果真用了想找回来怎么办?见救命的后悔药。

当然了,你只要开心不加softhard参数也是安全的(相当于使用了--mixed参数),只不过是撤消以后你的本次修改就会回到add之前的状态,你可以重新检视然后再做修改和commit

回退远程仓库

要是我们做的更过分一点,直接把这次commit直接给push怎么办?要是被发现就全完了,我们来看看github上的远程仓库。

upload successful

完了,真的提交了(我刚刚push的)让我们冷静下来,用撤消当前commit的方法先撤消本地的commit,这次我们来试试用hard参数来撤消

$ git reset --hard head^HEAD is now at 3f22a06 [+]add file time.txt$ git statusOn branch masterYour 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$ git push origin master --forceTotal 0 (delta 0), reused 0 (delta 0)To :pzqu/git_test.git+ 3d113a7...3f22a06 master -> master (forced update)

使用git reset --hard head^回滚到上一个commit使用git status查看现在的工作区情况,提示Your branch is behind 'origin/master' by 1 commit,代表成功表了上一次的提示状态,nothing to commit, working tree clean代表这次的修改全没了,清理的算是一个彻底。如果还想找回来怎么办,我们还真是有办法让你找回来的,见救命的后悔药。git push origin master --force命令强制提交到远程仓库(注意,如果是在团队合作的情况下,不到迫不得已不要给命令加--force参数) 让我们看看github

upload successful

真的撤消了远程仓库,长舒一口气。

暂存区(Stage)到工作区(Working Directory)

如果我们刚刚执行了git reset --soft或者add等的操作,把一些东西加到了我们的暂存区,比如日志文件,我们就要把他们从暂存区拿出来。

$ git statusOn branch masterYour branch is up to date with 'origin/master'.Changes to be committed:(use "git reset HEAD <file>..." to unstage)new file: mysql.log$ git reset -- mysql.log$ git statusOn branch masterYour branch is up to date with 'origin/master'.Untracked files:(use "git add <file>..." to include in what will be committed)mysql.lognothing added to commit but untracked files present (use "git add" to track)

status查看暂存区,里面有一个mysql.log被放进去了git reset -- mysql.logmysql.log取出来status可以看到真的取出来了 然后如果不要想这个文件的话再rm掉就好啦,但是如果这些文件每次自动生成都要用这种方式取出暂存区真的好累,我们可以用 git忽略不想提交的文件

回滚文件到某个提交

当我们想要把某个文件任意的回滚到某次提交上,而不改变其他文件的状态我们要怎么做呢?

我们有两种情况,一种是,只是想在工作区有修改的文件,直接丢弃掉他现在的修改;第二种是想把这个文件回滚到以前的某一次提交。我们先来说第一种:

取消文件在工作区的修改

$ cat time.txt10:41$ echo 18:51 > time.txt$ git statusOn branch masterYour branch is up to date with 'origin/master'.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: time.txtno changes added to commit (use "git add" and/or "git commit -a")$ cat time.txt18:51$ git checkout -- time.txt$ cat time.txt10:41

更新time.txt的内容,可以status看到他发生了变化git checkout -- time.txt, 取消这次在工作区的修改,如果他已经被add加到了暂存区,那么这个命令就没有用了,他的意思是取消本次在工作区的修改,去上一次保存的地方。如果没有add就回到和版本库一样的状态;如果已经加到了暂存区,又做了修改,那么就回加到暂存区后的状态将文件回滚到任意的版本我们这里说的把文件回滚到以前的某个版本的状态,完整的含义是保持其他文件的内容不变,改变这个文件到以前的某个版本,然后修改到自己满意的样子和做下一次的提交。

核心命令

git checkout [<options>] [<branch>] -- <file>...

我们还是用time.txt这个文件来做试验,先搞三个版本出来,在这里我已经搞好了,来看看:

版本1,time.txt内容00:50

commit 35b66ed8e3ae2c63cc4ebf323831e3b917d2b1d4 (HEAD -> master, origin/master, origin/HEAD)Author: pzqu <pzqu@>Date: Sun Dec 23 00:51:54 +0800[*]update time to 00:50

版本2,time.txt内容18:51

commit 856a74084bbf9b678467b2615b6c1f6bd686ecffAuthor: pzqu <pzqu@>Date: Sat Dec 22 19:39:19 +0800[*]update time to 18:51

版本3,time.txt内容10:41

commit 3f22a0639f8d79bd4e329442f181342465dbf0b6Author: pzqu <pzqu@>Date: Tue Dec 18 10:42:29 +0800[+]add file time.txt

现在的是版本1,我们把版本3检出试试。

$ git checkout 3f22a0639f8d -- time.txt$ cat time.txt10:41$ git statusOn branch masterYour branch is up to date with 'origin/master'.Changes to be committed:(use "git reset HEAD <file>..." to unstage)modified: time.txt

使用checkout+commit id+-- filename的组合,横跨版本2把历史版本3的time.txt搞出来了查看状态,time.txt被改变了

我们来把time.txt恢复到版本1,同样的方法,因为版本1是上一次提交我们可以省略掉版本号

$ git checkout -- time.txt$ cat time.txt00:50

看到了吧!只要用git checkout commit_id -- filename的组合,想搞出哪个文件历史版本就搞出哪个。

到了这里,你可能会很懵比,resetcheckout命令真的好像啊!都可以用来做撤消

checkout语义上是把什么东西取出来,所以此命令用于从历史提交(或者暂存区域)中拷贝文件到工作目录,也可用于切换分支。reset语义上是重新设置,所以此命令把当前分支指向另一个位置,并且有选择的变动工作目录和索引。也用来在从历史仓库中复制文件到索引,而不动工作目录。

还想不通可以给我发邮件:pzqu@

救命的后悔药

来到这里我已经很清楚的你的现况了,你的代码丢了现在一定非常的着急,不要慌,总是有办法找回他们的。但是前提是要保证你的项目根目录下.git文件夹是完整的,要是手动删除了里面的一些东西那就真完了。还要保证一点,你的代码以前是有过git追踪的,最少add

找回你丢失的历史记录

Git提供了一个命令git reflog用来记录你的每一次命令,贴个图吧直观点:

upload successful

有没有发现,git reflog里的全部都是和改变目录树有关的,比如commit rebase reset merge,也就是说一定要有改变目录树的操作才恢复的回来像add这种操作就不能恢复了吗?那肯定不是,只是要用更麻烦点的方式来恢复git log是一样的,也可以看到所有分支的历史提交,不一样的是看不到已经被删除的commit记录和reset rebase merge的操作 我们可以看到git reflog前面的就是commit id,现在我们就可以用之前介绍过的方法来回滚版本了,撤消当前commit

$ git reset --hard 856a740HEAD is now at 856a740 [*]update time to 18:51$ git log -1commit 856a74084bbf9b678467b2615b6c1f6bd686ecff (HEAD -> master)Author: pzqu <pzqu@>Date: Sat Dec 22 19:39:19 +0800[*]update time to 18:51$ git reset --hard 35b66edHEAD is now at 35b66ed [*]update time to 00:50$ git log -2commit 35b66ed8e3ae2c63cc4ebf323831e3b917d2b1d4 (HEAD -> master, origin/master, origin/HEAD)Author: pzqu <pzqu@>Date: Sun Dec 23 00:51:54 +0800[*]update time to 00:50commit 856a74084bbf9b678467b2615b6c1f6bd686ecffAuthor: pzqu <pzqu@>Date: Sat Dec 22 19:39:19 +0800[*]update time to 18:51

根据git reflog返回的结果,用git reset --hard commit_id回退到856a740这个版本git log -1看近一行的日志,可以看到目前就在这了再根据git reflog的结果,用git reset --hard 35b66ed跑到这次提交git log -2看到两次提交的日志,我们就这么再穿梭过来了,就是这么爽 但是我们如果只是想把此提交给找回来,恢复他,那还是不要用reset的方式,可以用cherry-pick或者merge来做合并

找回忘记提交的历史记录

你之前没有commit过的文件,被删除掉了,或者被reset --hard的时候搞没了,这种情况可以说是相当的难搞了,所幸你以前做过add的操作把他放到过暂存区,那我们来试试找回来,先来创建一个灾难现场

$ echo 'my lose message' > lose_file.txt$ git add lose_file.txt$ git statusOn branch masterYour branch is up to date with 'origin/master'.Changes to be committed:(use "git reset HEAD <file>..." to unstage)new file: lose_file.txt$ git reset --hard 35b66ed8HEAD is now at 35b66ed [*]update time to 00:50$ git statusOn branch masterYour branch is up to date with 'origin/master'.nothing to commit, working tree clean$ lsREADME.mdneed_stash.txt share_file.txt time.txt

创建一个叫lose_file.txt的文件并写入内容my lose message,并把他加到暂存区用git reset --hard 35b66ed8用丢弃一切修改的方式来使现在的工作区恢复到35b66ed8版本,因为还没提交所以也就是恢复到当前的(head)版本。我们用statusls再看,这个叫lose_file.txt的文件真的没了,完蛋了,第一反应用刚刚学到的命令git reflow会发现根本就不好使

核心命令:git fsck --lost-found,他会通过一些神奇的方式把历史操作过的文件以某种算法算出来加到.git/lost-found文件夹里

$ git fsck --lost-foundChecking object directories: 100% (256/256), done.Checking objects: 100% (3/3), done.dangling blob 7f5965523d2b9e850b39eb46e8e0f7c5755f6719dangling commit fdbb19cf4c5177003ea6610afd35cda117a41109dangling commit 8be46aa83f0fe90317b0c6b9c201ad994f8caeafdangling blob 11400c1d56142615deba941a7577d18f830f4d85dangling tree 3bd4c055afedc51df0326def49cf85af15994323dangling commit 3d113a773771c09b7c3bf34b9e974a697e04210adangling commit bfdc065df8adc44c8b69fa6826e75c5991e6cad0dangling tree c96ff73cb25b57ac49666a3e1e45e0abb8913296dangling blob d6d03143986adf15c806df227389947cf46bc6dedangling commit 7aa21bc382cdebe6371278d1af1041028b8a2b09

这里涉及到git的一些低层的知识,我们可以看到这里有blob、commit、tree类型的数据,还有tag等类型的。他们是什么含义呢?

upload successful

blob组件并不会对文件信息进行存储,而是对文件的内容进行记录commit组件在每次提交之后都会生成,当我们进行commit之后,首先会创建一个commit组件,之后把所有的文件信息创建一个tree组件,所以哪个blob代表什么文件都可以在tree里找到 我们来看看怎么恢复刚刚不见了的lose_file.txt文件,在上面执行完git fsck --lost-found命令,返回的第一行blob我们看看他的内容

git show 7f5965523d2b9e850b39eb46e8e0f7c5755f6719my lose messagegit show 7f5965523d2b9e850b39eb46e8e0f7c5755f6719 > lose_file.txt$ lsREADME.mdlose_file.txt need_stash.txt share_file.txt time.txt

看到没有,就是我们丢失的文件内容,这样就找回来了! 我们再来看看commit tree的内容$ git cat-file -p fdbb19cf4c5177003ea6610afd35cda117a41109 tree 673f696143eb74ac5e82a46ca61438b2b2d3bbf4 parent e278392ccbf4361f27dc338c854c8a03daab8c49 parent 7b54a8ae74be7192586568c6e36dc5a813ff47cf author pzqu pzqu@ 1544951197 +0800 committer pzqu pzqu@ 1544951197 +0800 Merge branch 'master' of :pzqu/git_test $ git ls-tree 3bd4c055afedc51df0326def49cf85af15994323 100644 blob c44be63b27a3ef835a0386a62ed168c91e680e87 share_file.txt用git cat-file -p可以看到commit的内容,可以选择把这个commit合并到我们的分支里,还是reset merge rebase cherry-pick这些命令来合commitgit ls-tree列出tree下面的文件名和id的记录信息,然后就可以根据这些来恢复文件了

后记:

如果你发现执行git fsck --lost-found的输出找不到你想要的,那么在执行完git fsck --lost-found后会出现一堆文件 在 .git/lost-found 文件夹里,我们不管他。可以用以下命令来输出近期修改的文件

$ find .git/objects -type f | xargs ls -lt | sed 3q-r--r--r-- 1 pzqu staff 32 12 23 12:19 .git/objects/7f/5965523d2b9e850b39eb46e8e0f7c5755f6719-r--r--r-- 1 pzqu staff 15 12 23 01:51 .git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391-r--r--r-- 1 pzqu staff 162 12 23 00:51 .git/objects/35/b66ed8e3ae2c63cc4ebf323831e3b917d2b1d4$ git cat-file -t 7f5965523d2b9e850b39eb46e8e0f7c5755f6719blob$ git cat-file -p 7f5965523d2b9e850b39eb46e8e0f7c5755f6719my lose message$ git cat-file -t b2484b5ab58c5cb6ecd92dacc09b41b78e9b0001tree$ git cat-file -p b2484b5ab58c5cb6ecd92dacc09b41b78e9b0001100644 blob f9894f4195f4854cfc3e3c55960200adebbc3ac5 README.md100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 need_stash.txt100644 blob 83f50ec84c00f5935da8089bac192171cfda8621 share_file.txt100644 blob f0664bd6a49e268d3db47c508b08d865bc25f7bb time.txt

这里用find .git/objects -type f | xargs ls -lt | sed 3q返回了近3个修改的文件,想要更多就改3q这个数值,比如你想输出100个就用100qgit cat-file -t 7f5965523d2b9e850b39eb46e8e0f7c5755f6719就能看见文件类型 把最后一个/去掉 复制从objects/ 后面的所有东西放在-t后面git cat-file -p id就能看见文件内容,是不是很爽

漏提交

有时候会碰到我们已经commit但是有修改忘记了提交,想把他们放在刚刚的commit里面,这种时候怎么做呢?

$ git log --name-status --pretty=oneline -135b66ed8e3ae2c63cc4ebf323831e3b917d2b1d4 (HEAD -> master, origin/master, origin/HEAD) [*]update time to 00:50M time.txt$ git statusOn branch masterYour branch is up to date with 'origin/master'.Changes to be committed:(use "git reset HEAD <file>..." to unstage)new file: lose_file.txtnew file: test_amend.txt$ git commit --amend --no-edit[master 31cc277] [*]update time to 00:50Date: Sun Dec 23 00:51:54 +08003 files changed, 2 insertions(+), 1 deletion(-)create mode 100644 lose_file.txtcreate mode 100644 test_amend.txt$ git log --name-status --pretty=oneline -131cc2774f0668b5b7c049a404284b19e9b40dc5d (HEAD -> master) [*]update time to 00:50A lose_file.txtA test_amend.txtM time.txt

查看文件提交日志只有time.txtstage里还有新的修改在使用git commit --amend --no-edit合并到上一个提交里,如果不加--no-edit参数的话,会提示你来修改commit提示信息(这个命令也可以用在重复编辑commit message)。查看日志,合并提交成功!

tag标签

创建一个tag

标签是一个类似于快照的东西,常常用于测试和发布版本。所以我们常常把tag名以版本号来命名,比如:v1.0beat1这样

我们怎么创建标签呢?首先先切换到想打标签的分支,然后直接打就可以了。

$ git branchdev/pzqumaster* release_v1.0$ git tag -a release_v1.0 -m "release v1.0"$ git tag release_v1.1$ git tagrelease_v1.0release_v1.1$ git push --tagsCounting objects: 2, done.Writing objects: 100% (2/2), 158 bytes | 158.00 KiB/s, done.Total 2 (delta 0), reused 0 (delta 0)To :pzqu/git_test.git* [new tag] release_v1.0 -> release_v1.0* [new tag] release_v1.1 -> release_v1.1

切换到想打tag的分支创建名为release_v1.0带有信息release v1.0tag创建的不带有tag的提交信息的release_v1.1git tag查看tag推送本地全部tag

也可以推送单个tag

$ git push origin release_v1.1Total 0 (delta 0), reused 0 (delta 0)To :pzqu/git_test.git* [new tag] release_v1.1 -> release_v1.1

我们来删除tag

$ git tag -d release_v1.0Deleted tag 'release_v1.0' (was eb5d177)$ git push origin :refs/tags/release_v1.0To :pzqu/git_test.git- [deleted] release_v1.0$ git tagrelease_v1.1

本地删除名为release_v1.0tag远程删除名为release_v1.0tag

对历史提交打tag

先看看当前的log

31cc277 (HEAD -> release_v1.0, tag: release_v1.1, origin/release_v1.0, master) [*]update time to 00:50856a740 [*]update time to 18:513f22a06 [+]add file time.txt4558a25 (origin/dev/pzqu, dev/pzqu) [*]test stashd9e018e [*]merge master to dev/pzqu

比方说要对[*]update time to 18:51这次提交打标签,它对应的commit id是856a740,敲入命令:

$ git tag v.9 856a740$ git log --pretty=oneline --abbrev-commit31cc277 (HEAD -> release_v1.0, tag: release_v1.1, origin/release_v1.0, master) [*]update time to 00:50856a740 (tag: v0.9) [*]update time to 18:51

成功打上

git忽略不想提交的文件

我们有两种情况,一种是我们根本就不想这些文件出现在git库里比如日志文件;另一种是git远程仓库里有这些文件,就像通用的配置文件,我们必须要在本地修改配置来适应运行环境,这种情况下我们不想每次提交的时候都去跟踪这些文件。

忽略自动生成的垃圾文件、中间文件、敏感信息文件

忽略文件的原则是:

忽略操作系统自动生成的文件,比如缩略图等;忽略编译生成的中间文件、可执行文件等,也就是如果一个文件是通过另一个文件自动生成的,那自动生成的文件就没必要放进版本库,比如Java编译产生的.class文件;忽略你自己的带有敏感信息的配置文件,比如存放口令的配置文件。

我们要怎么做呢?

在Git工作区的根目录下创建一个特殊的.gitignore文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件。$ echo ".log" > .gitignore$ touch test.log$ touch test2.log$ ls -a . .git README.md need_stash.txt test.log test_amend.txt .. .gitignore lose_file.txt share_file.txt test2.log time.txt$ git status On branch release_v1.0 nothing to commit, working tree clean创建并写入忽略规则*.log忽略全部以.log为后缀的文件 * 创建了test.logtest2.log*status查看,真是工作区是clean,新创建的文件没有被跟踪

忽略远程存在,本地不想与远程同步的文件

添加跟踪忽略

核心命令:

git update-index —assume-unchanged 文件名

upload successful

创建time.txt文件并写入10:41,提交到远程仓库使用命令git update-index —assume-unchangedtime.txt加到忽略名单里修改time.txt的内容为10:43status查看确实没有被跟踪 看远程仓库

upload successful

取消跟踪忽略

核心命令:

git update-index —no-assume-unchanged 文件名

upload successful

pull同步远程仓库,真的没有更新刚刚被添加跟踪忽略的文件git update-index —no-assume-unchanged取消跟踪忽略status查看,出现文件的跟踪

查看跟踪记录

如果忘记了哪些文件被自己本地跟踪

upload successful

使用命令git update-index —assume-unchangedtime.txt加到忽略名单里使用git ls-files -v| grep '^h\ '命令可以看到小写h代表本地不跟踪的文件

小结

学完本文章,你将学会

撤消commit,回滚暂存区,回滚工作区、回退远程仓库两种方法找回不小心丢失的文件提交的时候漏了文件,修改commit的提交信息tag操作,创建、创建有描述信息的tag、删除tag、删除远程tag、推送本地单个tag和全部taggit忽略自动生成的垃圾文件、中间文件、敏感信息文件;忽略远程存在,本地不想与远程同步的文件并恢复跟踪和查看哪些文件被跟踪

注意事项

理论上,git日常用到的命令是 diff show fetch rebase pull push checkout commit status 等,这些命令都不会导致代码丢失,假如害怕代码丢失,可以预先commit一次,再进行修改,但切记

不可使用自己不熟悉的命令 任何命令,不要加上-f的强制参数,否则可能导致代码丢失

建议多使用命令行,不要使用图形界面操作

下集

引用

git官网

廖雪峰的官方网站-git篇

hexo博客部署到vps

关于git reset --hard这个命令的惨痛教训

Git 基础再学习之:git checkout -- file

如何理解git checkout -- file和git reset HEAD -- file

此文已由腾讯云+社区在各渠道发布

获取更多新鲜技术干货,可以关注我们腾讯云技术社区-云加社区官方号及知乎机构号

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。