我先开一会儿吐槽大会,从大四进课题组做科研开始,Git 这东西我用了一年,根本用不明白。
我搞不明白的一个重要原因就是,命令的功能太杂,有时候一个需求可以用好几种命令解决,而且有的命令还 tm 有别名。这导致什么问题呢,我在网上找到的答案五花八门,竟然都能达成目的,难以找到规律,毫无套路可言。
以前我用 Git,就知道 add .,然后 commit -m,最后 push origin master 一套带走,或者就是把 Git 作为下载器,去 clone 别人的项目。可是在日常项目使用过程中经常遇到因为手残把代码弄错了需要恢复到原来的版本的问题,对于git的学习,我的经验是,一是投入实践,二是化复杂为简单。
所以本文不是一个大而全 Git 命令的使用手册,而是根据实际工作中最常见问题,提供小而美的解决方案,仅仅涉及四个命令:add,commit,reset,checkout。
远程仓库涉及的三个命令:fetch,pull,clone
首先,在进入 Git 的各种操作之前,一定要明白 git 的三个「分区」是什么,否则的话你一定没办法真正理解 Git 的原理。Git 的三个分区分别是:working directory,stage/index area,commit history。
working directory是「工作目录」,也就是我们肉眼能够看到的文件,后文我们称其为work dir区。
当我们在work dir中执行git add相关命令后,就会把work dir中的修改添加到「暂存区」stage area(或者叫index area)中去,后文我们称暂存区为stage区。
当stage中存在修改时,我们使用git commit相关命令之后,就会把stage中的修改保存到「提交历史」commit history中,也就是HEAD指针指向的位置。后文我们称「提交历史」为history区。
关于 commit history 我们多说几句,任何修改只要进入 commit history,基本可以认为永远不会丢失了。每个 commit 都有一个唯一的 Hash 值,我们经常说的 HEAD 或者 master 分支,都可以理解为一个指向某个 commit 的指针。
work dir 和 stage 区域的状态,可以通过命令 git status 来查看,history 区域的提交历史可以通过 git log 命令来查看。
好的,如果上面的内容你都能够理解,那么本文就完全围绕这三个概念展开,下面就是一个关系图:
$ touch test1.txt test2.txt
$ git add .
$ git status
On branch master
Changes to be committed:
new file: test1.txt
new file: test2.txt
然后我又修改了test1.txt文件。
$ echo hello world >>test1.txt
$ git status
On branch master
Changes to be committed:
new file: test1.txt
new file: test2.txt
Changes not staged for commit:
modified: test1.txt
现在,我后悔了,我认为不应该修改 test1.txt,我想把它还原成 stage中的空文件,怎么办?
答案是,使用 checkout 命令:
$ git checkout test1.txt
Updated 1 path from the index
$ git status
On branch master
Changes to be committed:
new file: test1.txt
new file: test2.txt
输出显示从index区(也就是== stage == 区)更新了一个文件,也就是把work dir中 test1.txt 文件还原成了stage中的状态(一个空文件)。
当然,如果work dir中被修改的文件很多,可以使用通配符全部恢复成stage:
git checkout *
有一点需要指出的是,checkout 命令只会把被「修改」的文件恢复成 stage 的状态,如果 work dir 中新增了新文件,你使用 git checkout . 是不会删除新文件的。
$ git commit -m '一些描述'
再简单提一些常见场景, 比如说 commit 完之后,突然发现一些错别字需要修改,又不想为改几个错别字而新开一个 commit 到 history 区,那么就可以使用下面这个命令:
$ git commit --amend
这样就是把错别字的修改和之前的那个 commit 中的修改合并,作为一个 commit 提交到 history 区。
这个需求很常见,比如说我用了一个 git add . 一股脑把所有修改加入 stage,但是突然想起来文件 test1.txt 中的代码我还没写完,不应该把它 commit 到 history 区,所以我得把它从 stage 中撤销,等后面我写完了再提交。
$ echo aaa >>test1.txt; echo bbb >>test2.txt;
$ git add .
$ git status
On branch master
Changes to be committed:
modified: test1.txt
modified: test2.txt
如何把 test1.txt 从 stage 区还原出来呢?可以使用 git reset 命令:
$ git reset test1.txt
$ git status
On branch master
Changes to be committed:
modified: test2.txt
Changes not staged for commit:
modified: test1.txt
你看,这样就可以把== test1.txt == 文件从 stage 区移出,这时候进行git commit相关的操作就不会把这个文件一起提交到 history 区了。
上面的这个命令是一个简写,实际上 reset 命令的完整写法如下:
$ git reset --mixed HEAD test1.txt
其中,mixed是一个模式(mode)参数,如果 reset 省略这个选项的话默认是 mixed 模式;HEAD指定了一个历史提交的 hash 值;test1.txt 指定了一个或者多个文件。
该命令的自然语言描述是:不改变 work dir 中的任何数据,将 stage 区域中的 a.txt 文件还原成 HEAD 指向的 commit history 中的样子。就相当于把对 a.txt 的修改从 stage 区撤销,但依然保存在 work dir 中,变为 unstage 的状态。
依然是使用 checkout 命令,但是和之前的使用方式有一些不同:
$ git checkout HEAD .
Updated 12 paths from d480c4f
这样,work dir和stage中所有的「修改」都会被撤销,恢复成HEAD指向的那个history commit。
注意,类似之前通过stage恢复work dir的checkout命令,这里撤销的也只是修改,新增的文件不会被撤销。
当然,只要找到任意一个 commit 的 HASH 值,checkout 命令可就以将文件恢复成任一个 history commit 中的样子。
建议谨慎使用。这个操作会将指定文件在 work dir 的数据恢复成指定 commit 的样子,且会删除该文件在 stage 中的数据,都无法恢复,所以应该慎重使用。
ssh-keygen -t ecdsa -b 521 -C "825830916@qq.com"
gedit ~/.ssh/id_ecdsa.pub
复制id_ecdsa.pub的全部内容,这种SSH传输方式非常快速便捷。
在github setting之中找到SSH and GPG keys添加。
将id_ecdsa.pub中的内容复制到新建的 SSHkey中。
git fetch和git pull都可以将远端仓库更新至本地那么他们之间有何区别?想要弄清楚这个问题有有几个概念不得不提。
FETCH_HEAD: 是一个版本链接,记录在本地的一个文件中,指向着目前已经从远程仓库取下来的分支的末端版本。
commit-id:在每次本地工作完成后,都会做一个git commit 操作来保存当前工作到本地的repo, 此时会产生一个commit-id,这是一个能唯一标识一个版本的序列号。 在使用git push后,这个序列号还会同步到远程仓库。
有了以上的概念再来说说git fetch
git fetch:这将更新git remote 中所有的远程仓库所包含分支的最新commit-id, 将其记录到.git/FETCH_HEAD文件中
git pull : 首先,基于本地的FETCH_HEAD记录,比对本地的FETCH_HEAD记录与远程仓库的版本号,然后git fetch 获得当前指向的远程分支的后续版本的数据,然后再利用git merge将其与本地的当前分支合并。所以可以认为git pull是git fetch和git merge两个步骤的结合。
因此,git fetch是从远程获取最新版本到本地,但不会自动merge。
而git pull则是会获取所有远程索引并合并到本地分支中来。效果相同时git pull将更为快捷。
这里拿我前些天在github仓库新建的项目为例。
echo "# DIY_CPU">>README.md
git init
git add README.md
git commit -m "first commit"
git config --global user.email "you@example.com"
git branch -M main
git remote add origin git@github.com:tongjiaxuan666/DIY_CPU.git
git push -u origin main
其中origin远程在本地的别名。
通过push命令将本地的history HEAD指向的版本提交上去。
git fetch --all
git pull
现在本地版本就是远程的最新版本了。
四. git在线练习平台这是个叫做 Learning Git Branching 的项目,是我一定要推荐的:
这个网站的教程不是给你举那种修改文件的细节例子,而是将每次 commit 都抽象成树的节点,用动画闯关的形式,让你自由使用 Git 命令完成目标:
所有 Git 分支都被可视化了,你只要在左侧的命令行输入 Git 命令,分支会进行相应的变化,只要达成任务目标,你就过关啦!网站还会记录你的命令数,试试能不能以最少的命令数过关!
我一开始以为这个教程只包含本地 Git 仓库的版本管理,后来我惊奇地发现它还有远程仓库的操作教程!
网站地址:https://learngitbranching.js.org
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧