2013.10.30(3)——— git学习之文件状态及其操作

2013.10.30(3)——— git学习之文件状态及其操作

首先介绍 Git 管理项目时,文件流转的三个工作区域:Git 的本地数据目录,工作目录以及暂存区域。
对于任何一个文件,在Git 内都只有三种状态:已提交(committed),已修改(modified)和已暂存(staged)。已提交表示该文件已经被安全地保存在本地数据库中了;已修改表示修改了某个文件,但还没有提交保存;已暂存表示把已修改的文件放在下次提交时要保存的清单中。
每个项目都有一个git 目录,它是Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。
从项目中取出某个版本的所有文件和目录,用以开始后续工作的叫做工作目录。这些文件实际上都是从git目录中的压缩对象数据库中提取出来的,接下来就可以在工作目录中对这些文件进行编辑。
所谓的暂存区域只不过是个简单的文件,一般都放在git 目录中。有时候人们会把这个文件叫做索引文件,不过标准说法还是叫暂存区域。
基本的Git 工作流程如下所示:


[img]http://dl2.iteye.com/upload/attachment/0090/7085/769e7f0b-916f-35e2-acf1-9bca7e852e30.png[/img]
1. 在工作目录中修改某些文件。
2. 对这些修改了的文件作快照,并保存到暂存区域。
3. 提交更新,将保存在暂存区域的文件快照转储到git 目录中。
所以,我们可以从文件所处的位置来判断状态:如果是git 目录中保存着的特定版本文件,就属于已提交状态;如果作了修改并已放入暂存区域,就属于已暂存状态;如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。


工作目录下面的所有文件都不外乎这两种状态:已跟踪或未跟踪。[color=blue]已跟踪[/color]的文件是指本来就被纳入版本控制管理的文件,在上次快照中有它们的记录,工作一段时间后,它们的状态可能是[color=blue]未修改[/color],[color=blue]已修改[/color]或者[color=blue]已放入暂存区[/color]。而所有其他文件都属于[color=blue]未跟踪[/color]文件。它们既没有上次更新时的快照,也不在当前的暂存区域。
初次克隆某个仓库时,工作目录中的所有文件都属于已跟踪文件,且状态为未修改。
在编辑过某些文件之后,Git 将这些文件标为已修改。我们逐步把这些修改过的文件放到暂存区域,然后等最后一次性提交暂存区域的所有文件更新,如此重复。所以使用Git 时的文件状态变化周期如图所示。


[img]http://dl2.iteye.com/upload/attachment/0090/6838/d1244992-c5f4-3dfb-b5ad-ca59e2733ea6.png[/img]

先介绍几个个命令
检查当前文件状态
git status

git add 命令 这是个多功能命令,根据目标文件的状态不同,此命令的效果也不同:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等

gid diff 文件

gid diff com/lp/test/A.java

这可以看出来A文件的具体修改了那些内容




[color=red]1、未跟踪[/color]

我在git_workspace下面新建了5.cpp

$ git status
# On branch master
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# 5.cpp
nothing added to commit but untracked files present (use "git add" to track)


执行git status 结果如上,
[color=red]“Untracked files”这行下面的都是未跟踪[/color]
可以看出来,5.cpp 处于未跟踪状态untracked


[color=red]2、已跟踪[/color]

$ git add 5.cpp


再次查看状态
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: 5.cpp
#

[color=red]只要在“Changes to be committed” 这行下面的,就说明是已暂存状态
可以看出来 5.cpp 已跟踪并且已暂存到索引,这个时候 执行 git commit 就会提交到本地库中,并且5.cpp转变为未修改状态[/color]


[color=red]3、暂存已修改文件[/color]

5.cpp已经提交了 这个时候 我修改它


$ git status
# On branch 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: 1.cpp
#
no changes added to commit (use "git add" and/or "git commit -a")



"Changes not staged for commit"这行下面 说明已跟踪文件的内容发生了变
化,但还没有放到暂存区。要暂存这次更新,需要运行git add 命令

$ git add 1.cpp


$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: 1.cpp
#


现在两个文件都已暂存,下次提交时就会一并记录到仓库

接下来 我又给1.cpp添加了注释,然后 看下状态
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: 1.cpp
#
# 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: 1.cpp
#


[color=blue]1.cpp 文件出现了两次!一次算未暂存,一次算已暂存,这怎么可能呢?好吧,实际上Git只不过暂存了你运行git add 命令时的版本,如果现在提交,那么提交的是添加注释前的版本,而非当前工作目录中的版本。所以,运行了git add 之后又作了修订的文件,需要重新运行git add 把最新版本重新暂存起来:[/color]

$ git add 1.cpp

$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: 1.cpp
#



[color=red]4、提交更新[/color]
一定要确认还有什么修改过的或新建的文件还没有
git add 过,否则提交的时候不会记录这些还没暂存起来的变化。所以,每次准备提交前,先用git status 看下,是不是都已暂存起来了,然后再运行提交命令git commit:

$ git commit


这种方式会启动文本编辑器以便输入本次提交的说明
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: 1.cpp


默认的提交消息包含最后一次运行git status 的输出,如果觉得这还不够,可以用-v 选项将修改差异的每一行都包含到注释中来,退出编辑器时,Git 会丢掉注释行,将说明内容和本次更新提交到仓库。
也可以使用-m 参数后跟提交说明的方式,在一行命令中提交更新

$ git commit -m "modified 1.cpp"


提交后它会告诉你,当前是在哪个分支(master)提交的,本次提交的完整SHA-1 校验和是什么(463dc4f),以及在本次提交中,有多少文件修订过,多少行添改和删改过。

$ git commit
[master 2b0ecca] aaaa
1 file changed, 3 insertions(+), 2 deletions(-)


还有一个常用的
$ git commit --amend


我要改一个bug,我修改完之后 commit一次,后来 我发现还得需要改,这个时候 可以用这个命令 这两次提交相当于一次提交了

[color=red]5、跳过使用暂存区域[/color]

尽管使用暂存区域的方式可以精心准备要提交的细节,但有时候这么做略显繁琐。Git 提供了一个跳过使用暂存区域的方式,只要在提交的时候,给git commit 加上-a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过git add 步骤

我先修改了2.cpp,然后查看状态
$ vim 2.cpp

Sun@COSTARTER /e/git_test.git (master)
$ 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 <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified: 2.cpp
#
no changes added to commit (use "git add" and/or "git commit -a")


然后 不暂存 直接提交
Sun@COSTARTER /e/git_test.git (master)
$ git commit -a -m "2.cpp"
[master 1cc195b] 2.cpp
1 file changed, 2 insertions(+), 1 deletion(-)


[color=red]6、移除文件[/color]

要从Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。可以用git rm 命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了。如下所示

$ git rm 3.cpp
rm '3.cpp'

$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: 3.cpp


另外,如果你手动从工作目录删除了4.cpp,然后查看状态

$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
# Changes not staged for commit:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: 4.cpp
#


然后再运行git rm 记录此次移除文件的操作:
$ git rm 4.cpp
rm '4.cpp'

Sun@COSTARTER /e/git_test.git (master)
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
# deleted: 4.cpp
#


还有一张情况,如果删除之前修改过并且已经放到暂存区域的话,则必须要用强制删除选项-f(译注:即force 的首字母),以防误删除文件后丢失修改的内容。
我修改5.cpp,并且加入暂存区,然后rm,但是会error,提示必须加上-f

$ vim 5.cpp
$ git add 5.cpp

$ git rm 5.cpp
error: the following file has changes staged in the index:
5.cpp
(use --cached to keep the file, or -f to force removal)

Sun@COSTARTER /e/git_test.git (master)
$ git rm -f 5.cpp
rm '5.cpp'

Sun@COSTARTER /e/git_test.git (master)
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: 3.cpp
# deleted: 4.cpp
# deleted: 5.cpp
#


最后一种情况,我们想把文件从Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。换句话说,仅是从跟踪清单中删除。比如一些大型日志文件或者一堆.a 编译文件,不小心纳入仓库后,要移除跟踪但不删除文件,以便稍后在.gitignore 文件中补上,用--cached 选项即可:

$ git rm --cached log.txt



[color=red]7、移动文件[/color]
当然 mv命令 也可以用来重命名
git mv README.txt README
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# renamed: README.txt -> README
#


这个命令相当于执行了3个命令
$ mv README.txt README
$ git rm README.txt
$ git add README



[color=red]8、查看提交历史[/color]
[color=blue]“j”向下浏览,“k”向上浏览,“q”退出[/color]
在提交了若干更新之后,又或者克隆了某个项目,想回顾下提交历史,可以使用git log 命令。
$ git log
commit 1cc195bdb278c075b71929a1d844fe5496c66a03
Author: lipeng <lipeng@xunlei.com>
Date: Thu Oct 31 10:56:41 2013 +0800

2.cpp

commit 2b0ecca2e214cb658225f39a5b44107d200772cc
Author: lipeng <lipeng@xunlei.com>
Date: Thu Oct 31 10:37:18 2013 +0800

aaaa

commit 59f729dc95a406c875362f878582e09d98c39103
Author: lipeng <lipeng@xunlei.com>
Date: Wed Oct 30 17:58:18 2013 +0800

1.cpp 5.cpp

commit bc48f51f6c5d57c170c8447fb2809b8b4a200811
Author: lipeng <lipeng@xunlei.com>
Date: Wed Oct 30 17:44:21 2013 +0800

5.cpp


默认不用任何参数的话,git log 会按提交时间列出所有的更新,最近的更新排在最上面。每次更新都有一个SHA-1 校验和、作者的名字和电子邮件地址、提交时间,最后缩进一个段落显示提交说明。


我们常用-p 选项展开显示每次提交的内容差异,用-2 则仅显示最近的两次更新:
$ git log -p -2
commit 1cc195bdb278c075b71929a1d844fe5496c66a03
Author: lipeng <lipeng@xunlei.com>
Date: Thu Oct 31 10:56:41 2013 +0800

2.cpp

diff --git a/2.cpp b/2.cpp
index 43f7119..9cb98b2 100644
--- a/2.cpp
+++ b/2.cpp
@@ -1,3 +1,4 @@
int main(){
+ printf("abc");
return 0;
-}
\ No newline at end of file
+}

commit 2b0ecca2e214cb658225f39a5b44107d200772cc
Author: lipeng <lipeng@xunlei.com>
Date: Thu Oct 31 10:37:18 2013 +0800

aaaa

在做代码审查,或者要快速浏览其他协作者提交的更新都作了哪些改动时,就可以用这个选项。此外,还有许多摘要选项可以用,比如--stat,仅显示简要的增改行数统计,每个提交都列出了修改过的文件,以及其中添加和移除的行数,并在最后列出所有增减行数小计
$ git log --stat -2
commit 1cc195bdb278c075b71929a1d844fe5496c66a03
Author: lipeng <lipeng@xunlei.com>
Date: Thu Oct 31 10:56:41 2013 +0800

2.cpp

2.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

commit 2b0ecca2e214cb658225f39a5b44107d200772cc
Author: lipeng <lipeng@xunlei.com>
Date: Thu Oct 31 10:37:18 2013 +0800

aaaa

1.cpp | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)


还有个常用的--pretty 选项,可以指定使用完全不同于默认格式的方式展示提交历史。比如用oneline 将每个提交放在一行显示,这在提交数很大时非常有用。另外还有short,full 和fuller 可以用,展示的信息或多或少有些不同。但最有意思的是format,可以定制要显示的记录格式,这样的输出便于后期编程提取分析,像这样:
$ git log --pretty=format:"%h - %an, %ar : %s"
1cc195b - lipeng, 43 minutes ago : 2.cpp
2b0ecca - lipeng, 62 minutes ago : aaaa
59f729d - lipeng, 18 hours ago : 1.cpp 5.cpp
bc48f51 - lipeng, 18 hours ago : 5.cpp
3952ae8 - lipeng, 19 hours ago : init

$ git log --pretty=format:"%h - %an, %ad : %s"
1cc195b - lipeng, Thu Oct 31 10:56:41 2013 +0800 : 2.cpp
2b0ecca - lipeng, Thu Oct 31 10:37:18 2013 +0800 : aaaa
59f729d - lipeng, Wed Oct 30 17:58:18 2013 +0800 : 1.cpp 5.cpp
bc48f51 - lipeng, Wed Oct 30 17:44:21 2013 +0800 : 5.cpp
3952ae8 - lipeng, Wed Oct 30 16:52:27 2013 +0800 : init


产检的format格式占位符如下:

[img]http://dl2.iteye.com/upload/attachment/0090/7146/5e186aea-eee9-3f34-8475-92ddf70ab9b6.png[/img]

除了定制输出格式的选项之外,git log 还有许多非常实用的限制输出长度的选项,也就是只输出部分提交信息。之前我们已经看到过-2 了,它只显示最近的两条提交,实际上,这是-<n> 选项的写法,其中的n 可以是任何自然数,表示仅显示最近的若干条提交。不过实践中我们是不太用这个选项的,Git 在输出所有提交时会自动调用分页程序(pager),要看更早的更新只需翻到下页即可。另外还有按照时间作限制的选项,比如--since 和--until。下面的命令列出所有最近两周内的提交:
$ git log --since=2.weeks


其他常用的类似选项
选项说明
-(n) 仅显示最近的n 条提交
--since, --after 仅显示指定时间之后的提交。
--until, --before 仅显示指定时间之前的提交。
--author 仅显示指定作者相关的提交。
--committer 仅显示指定提交者相关的提交。


来看一个实际的例子,如果要查看Git 仓库中,2008 年10 月期间,Junio Hamano 提交的但未合并的测
试脚本(位于项目的t/ 目录下的文件),可以用下面的查询命令:
$ git log --pretty="%h:%s" --author=gitster --since="2008-10-01" \
--before="2008-11-01" --no-merges -- t/


[color=red]9、撤消操作[/color]


[color=red]9.1、修改最后一次提交[/color]

有时候我们提交完了才发现漏掉了几个文件没有加,或者提交信息写错了。想要撤消刚才的提交操作,可以
使用--amend 选项重新提交:

$ git commit --amend


此命令将使用当前的暂存区域快照提交。如果刚才提交完没有作任何改动,直接运行此命令的话,相当于有机会重新编辑提交说明,而所提交的文件快照和之前的一样。
启动文本编辑器后,会看到上次提交时的说明,编辑它确认没问题后保存退出,就会使用新的提交说明覆盖刚才失误的提交。
如果刚才提交时忘了暂存某些修改,可以先补上暂存操作,然后再运行--amend 提交:

$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend


[color=red]9.2、取消已经暂存的文件[/color]

来看下面的例子,有两个修改过的文件,我们想要分开提交,但不小心用git add * 全加到了暂存区域。该如何撤消暂存其中的一个文件呢?git status 命令的输出会告诉你怎么做:
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commi
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: 3.cpp
# deleted: 4.cpp
# deleted: 5.cpp



$ git reset HEAD 3.cpp
Unstaged changes after reset:
D 3.cpp

Sun@COSTARTER /e/git_test.git (master)
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: 4.cpp
# deleted: 5.cpp
#
# Changes not staged for commit:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: 3.cpp
#


现在3.cpp 文件又回到了之前已修改未暂存的状态。

9.3、取消对文件的修改


现在 我修改1.cpp

$ vi 1.cpp

Sun@COSTARTER /e/git_test.git (master)
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: 4.cpp
# deleted: 5.cpp
#
# 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: 1.cpp
#

修改前:
int main(){
printf("1234");
printf("555");
return 0;
}


修改后:
int main(){
printf("1234");
printf("555");
pritnf("xiaodu");
return 0;
}


然后 取消修改

$ git checkout -- 1.cpp

Sun@COSTARTER /e/git_test.git (master)
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: 4.cpp
# deleted: 5.cpp



打开1.cpp 查看现在内容:
int main(){
printf("1234");
printf("555");
return 0;
}


可以看到,该文件已经恢复到修改前的版本

另外 如果已经处在已暂存状态,如果想取消修改 ,就需要先取消暂存,然后取消修改
例如

修改
$ vi 1.cpp

查看状态
Sun@COSTARTER /e/git_test.git (master)
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: 4.cpp
# deleted: 5.cpp
#
# 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: 1.cpp
#

加入暂存区
$ git add 1.cpp

查看状态
Sun@COSTARTER /e/git_test.git (master)
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: 1.cpp
# deleted: 4.cpp
# deleted: 5.cpp
#

取消暂存
Sun@COSTARTER /e/git_test.git (master)
$ git reset HEAD 1.cpp
Unstaged changes after reset:
M 1.cpp

查看状态
Sun@COSTARTER /e/git_test.git (master)
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: 4.cpp
# deleted: 5.cpp
#
# 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: 1.cpp
#


取消修改
Sun@COSTARTER /e/git_test.git (master)
$ git checkout -- 1.cpp

查看状态
Sun@COSTARTER /e/git_test.git (master)
$ git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
# (use "git push" to publish your local commits)
#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# deleted: 4.cpp
# deleted: 5.cpp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值