本文来自网易云社区
作者:任江英
你的本地仓库由 git 维护的三棵“树”组成。第一个是你的 工作目录,它持有实际文件;第二个是 暂存区(Index),它像个缓存区域,临时保存你的改动;最后是 HEAD,它指向你最后一次提交的结果。
1.工作区(working directory) 2.暂存区(stage index) 3.历史记录区(history)
它是你实际持有的文件,是你可以看得到的文件目录
它是一个包含文件索引的目录树,像是一个虚拟的工作区。在这个虚拟的工作区的目录树中,记录了文件名、文件的状态信息(时间戳,文件长度),文件的内容并不存储其中,而是保存在Git对象库(.git/objects)中,文件索引建立了文件和对象库中对象实体之间的对应
这个命令将工作目录中的变化添加到缓存区 它实际上做了两件事:
将本地文件的内容做一个快照并保存到Git的对象库中。
在这个图中,我们可以看到部分 Git 命令是如何影响工作区和暂存区(stage, index)的:
图中左侧为工作区,右侧为版本库。在版本库中标记为 "index" 的区域是暂存区,标记为 "master" 的是 master 分支所代表的目录树。 //图中我们可以看出此时 "HEAD" 实际是指向 master 分支的一个“游标”。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。 图中的 objects 标识的区域为 Git 的对象库,实际位于 ".git/objects" 目录下
当对工作区修改或新增文件,执行‘git add’命令时,暂存区的目录树被更新,同时工作区修改或新增的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中
git add <file>
git add <directory>
git add -p
将中的更改保存到暂存区 将下的更改保存到暂存区 开始交互式的保存,你可以选择文件的一部分保存到暂存区。它会向你展示一堆更改,然后等待你输入命令。y
将这块更改加入缓存;n
忽略这块更改; q
推出;
当执行这个操作时,暂存区的目录树(快照)将被写到版本库(对象库)中。 它记录的是快照,而不是记录差异
git commit
git commit -m 'message'
git commit -a
提交已缓存的快照,它会运行文本编辑器,等待你输入提交信息。当你完成输入后,保存文件,关闭编辑器,完成提交
提交快照。作为提交信息,不用在运动文本编辑器了。
提交一份包含工作目录所有更改的快照,相当于运行了git add
把所有当前目录下的文件加入stage暂存,然后再运行git commit
当执行“git status”命令扫描工作区改动的时候,先依据.git/index文件中记录的时间戳、长度等信息判断工作区文件是否改变。如果工作区的文件时间戳改变,说明文件的内容可能被改变,需要打开文件,读取文件内容,和更改前的原始文件相比较(本地文件和与之对应的object库中的文件的内容进行对比),判断文件内容是否被更改。如果文件内容没有改变,则将该文件新的时间戳记录到 .git/index 文件中。因为判断文件是否更改,使用时间戳、文件长度等信息进行比较要比通过文件内容比较要快的多,所以 Git 这样的实现方式可以让工作区状态扫描更快速的执行,这也是 Git 高效的因素之一。
git status
命令显示工作目录和暂存区的状态。你可以看到哪些被缓存了,哪些还没有,以及哪些还未被Git跟踪。status的输出不会告诉你已经提交的信息。如果你想看,就用git log
.
这个命令告诉你 git add
和git commit
的进展??
git log
命令显示已经提交的快照。
与git status的区别
git status
允许你查看工作目录和暂存区,git log
只作用于提交的项目历史。
git log -n <limit> //用 <limit> 限制提交的数量。比如 git log -n 3 只会显示 3 个提交。
git log --oneline //将每个提交压缩到一行。当你需要查看项目历史的上层情况时这会很有用。
git log --stat //除了 git log 信息之外,包含哪些文件被更改了,以及每个文件相对的增删行数。
git log -p //显示代表每个提交的一堆信息。显示每个提交全部的差异(diff),这也是项目历史中最详细的视图。
git log --author="<pattern>" //搜索特定作者的提交。<pattern> 可以是字符串或正则表达式。
git log --grep="<pattern>" //搜索提交信息匹配特定 <pattern> 的提交。<pattern> 可以是字符串或正则表达式。
git log <since>..<until> //只显示发生在 <since> 和 <until> 之间的提交。两个参数可以是提交 ID、分支名、HEAD 或是任何一种引用。
git log <file> //只显示包含特定文件的提交。查找特定文件的历史这样做会很方便。
git log --graph --decorate --oneline //还有一些有用的选项。--graph 标记会绘制一幅字符组成的图形,左边是提交,右边是提交信息。--decorate 标记会加上提交所在的分支名称和标签。--oneline 标记将提交信息显示在同一行,一目了然。
git log --author="John Smith" -p hello.py
这个命令会显示 John Smith 作者对 hello.py 文件所做的所有更改的差异比较(diff) ..句法是比较分支很有用的工具。下面的栗子显示了在 some-feature 分支而不在 master 分支的所有提交的概览。
git log --oneline master..some-feature
这个命令有三个不同的作用:检出文件,检出提交和检出分枝; 检出提交会使工作目录和这个提交完全匹配。你可以用它来查看项目之前的状态,而不改变当前的状态。检出文件使你能够查看某个特定文件的旧版本,而工作目录中剩下的文件不变。
git checkout master
切换到master分支
git checkout <commit> <file>
回复文件之前的版本。它将工作目录中的文件变成中那个文件的拷贝,并将它加入缓存区
git checkout <file>
恢复暂存区的指定文件到工作区
git checkout
恢复暂存区的所有文件到工作区
git checkout <commit>
更新工作目录中的所有文件,使得和某个特定提交中的文件一致;
使用git checkout
的好处是,当你建立了项目历史之后,git checkout是一种便捷的方式,来将保存的快照‘加载’到你的服务器上去。
检出之前的提交是一个只读操作。在查看旧版本的时候绝不会损坏你的仓库。你项目「当前」的状态在 master 上不会变化。在开发的正常阶段,HEAD 一般指向 master 或是其他的本地分支,但当你检出之前提交的时候,HEAD 就不再指向一个分支了————它直接指向一个提交。
另外,检出旧文件不影响你仓库的当前状态。你可以在新的快照中像其他文件一样重新提交旧版本。所以在效果上,git checkout
的这个用法可以将单个文件回滚到旧版本。
这个例子假定你开始了一个疯狂的实验,但你不确定你是否想要保留它。为了帮助你决定,你想看一看你开始实验之前的项目状态。首先,你需要找到你想要看的那个版本的 ID。
git log --oneline
假设你的项目历史看上去和下面一样:
b7119f2 继续做些丧心病狂的事
872fa7e 做些丧心病狂的事
a1e8fb5 对 hello.py 做了一些修改
435b61d 创建 hello.py
9773e52 初始导入
你可以这样使用 git checkout
来查看「对 hello.py 做了一些修改」这个提交:
git checkout a1e8fb5
这让你的工作目录和 a1e8fb5 提交所处的状态完全一致。你可以查看文件,编译项目,运行测试,甚至编辑文件而不需要考虑是否会影响项目的当前状态。你所做的一切 都不会 被保存到仓库中。为了继续开发,你需要回到你项目的「当前」状态:
git checkout master
这里假定了你默认在 master 分支上开发,我们会在以后的分支模型中详细讨论。
一旦你回到 master 分支之后,你可以使用 git revert
或 git reset
来回滚任何不想要的更改。
如果只是想把文件恢复到之前的版本,可以用这个操作。比如回复道之前一个提交的文件
git checkout a1e8fb5 hello.py
检出文件和检出提交不同的地方是:检出文件会影响到你项目的当前的状态的。被重新检出的文件会显示为‘需要提交的更改’,允许你会滚到文件之前的版本。
git revert
命令是用来撤销一个已经提交的快照。这个操作是撤销这个提交的更改,但是对这个更改后面的提交是保留的。这避免了Git历史项目的丢失,这一点对项目的版本历史和协作的可靠性是很重要的。
git revert <commit>//
生成一个撤消了 引入的修改的新提交,然后应用到当前分支。
也是撤销的一种方法,但是比较危险,因为它是一个永远撤销的动作,无法获得原来的样子。
git reset <file>
从缓存区移除特定文件,但不改变工作目录。它会取消这个文件的缓存,但不覆盖任何改变(如果缓存中和工作目录不一样时,会保留工作目录,只是把这个文件的从缓存中移除)
git reset
重设缓存区,匹配最近一次的提交,但工作目录不变,它会取消最近一次暂存的所有文件的缓存,但不更改工作项目的文件,只是给了一次重设缓存快照的机会。
git reset --hard
重设缓存区和工作目录,匹配最近的一次提交。除了取消缓存外,--hard
标记也重写了所有工作目录中的更改。换句话说,是清除了所有未提交的更改。
git reset <commit>
将当前分支的末端移到 ,将缓存区重设到这个提交,但不改变工作目录。所有 之后的更改会保留在工作目录中,这允许你用更干净、原子性的快照重新提交项目历史。就是将这个 commit之后的所有暂存都取消。
git reset --hard <commit>
将当前分支的末端移到 ,将缓存区和工作目录都重设到这个提交。它不仅清除了未提交的更改,同时还清除了 之后的所有提交.
git reset
这个操作中,没有加--hard
的是取消缓存或取消一系列的提交,工作目录不变。但是加上--hard
标记是想要重新再来的操作,取消了缓存还把工作目录给修改了。这两个都是永久的操作。
警告 当你操作过并把这个提交推送到公共仓库后,就不能使用git reset
,如果你使用这个之后,你会把这个之后的所有提交都撤销,势必会影响他人。
移除一个其他团队成员在上面继续开发的提交在协作时会引发严重的问题。当他们试着和你的仓库同步时,他们会发现项目历史的一部分突然消失了。下面的序列展示了如果你尝试重设公共提交时会发生什么。origin/master 是你本地 master 分支对应的中央仓库中的分支。
git clean
命令是将未跟踪的文件从你的工作目录中移除。这个也是个无法撤销的操作。
git clean -n
执行一次git clean的『演习』。它会告诉你那些文件在命令执行后会被移除,而不是真的删除它。
git clean -f
移除当前目录下未被跟踪的文件。-f(强制)标记是必需的,除非 clean.requireForce 配置项被设为了 false(默认为 true)。它 不会 删除 .gitignore 中指定的未跟踪的文件。
git clean -f <path>
移除未跟踪的文件,但限制在某个路径下。
git clean -df
移除未跟踪的文件,以及目录。
git clean -xf
移除当前目录下未跟踪的文件,以及 Git 一般忽略的文件。
例子 如果你在本地仓库中作死之后想要毁尸灭迹,git reset --hard
和 git clean -f
是你最好的选择。运行这两个命令使工作目录和最近的提交相匹配,让你在干净的状态下继续工作。
git remote git fetch git pull
git fetch
命令是将提交从远程库倒入到你的本地仓库。拉取下来的提交储存为远程分支,而不是我们一直使用的普通的本地分支,他并不会自动合并和修改你当前的工作。当你准备好时你必须手动将其合并入你的工作。
git fetch <remote> //拉取仓库中所有的分支。同时会从另一个仓库中下载所有需要的提交和文件。
git fetch <remote> <branch> //只拉取指定的分支
当你希望查看其他人的工作进展时,你需要 fetch。fetch 下来的内容表示为一个远程分支,因此不会影响你的本地开发。这是一个安全的方式,在整合进你的本地仓库之前,检查那些提交。类似于 svn update,你可以看到中央仓库的历史进展如何,但它不会强制你将这些进展合并入你的仓库。
git log --oneline master..origin/master
git pull --rebase <remote>
和上一个命令相同,但使用 git rebase 合并远程分支和本地分支,而不是使用 git merge。
git push <remote> <branch>
将指定的分支推送到 上,包括所有需要的提交和提交对象。它会在目标仓库中创建一个本地分支。为了防止你覆盖已有的提交,如果会导致目标仓库非快速向前合并时,Git 不允许你 push。
git push <remote> --force
和上一个命令相同,但即使会导致非快速向前合并也强制推送。除非你确定你所做的事,否则不要使用 --force 标记。
git push <remote> --all
将所有本地分支推送到指定的远程仓库。
git push <remote> --tags
当你推送一个分支或是使用 --all 选项时,标签不会被自动推送上去。--tags 将你所有的本地标签推送到远程仓库中去。
git branch
列出仓库中所有分支。
git branch -r //用来查看远程分支
git branch <branch>
创建一个名为 的分支。不会 自动切换到那个分支去。
git branch -d <branch>
删除指定分支。这是一个安全的操作,Git 会阻止你删除包含未合并更改的分支。
git branch -D <branch>
强制删除指定分支,即使包含未合并更改。如果你希望永远删除某条开发线的所有提交,你应该用这个命令。
git branch -m <branch>
将当前分支命名为 。
git diff
主要是看工作区和暂存区的不同 git diff --cached
查看暂存区和HEAD之间的区别(commit之后的和add之后的) git diff HEAD
查看工作区和HEAD的区别。
网易云免费体验馆,0成本体验20+款云产品!
更多网易研发、产品、运营经验分享请访问网易云社区。