(0)Git目录结构及版本控制原理
一个Git项目中文件的状态大概分成下面的两大类,而第二大类又分为三小类:
1,未被跟踪的文件(untracked file)
2,已被跟踪的文件(tracked file)
2.1,被修改但未被暂存的文件(changed but not updated或modified)
2.2,已暂存可以被提交的文件(changes to be committed 或staged)
2.3,自上次提交以来,未修改的文件(clean 或 unmodified)
Git把它所管理的所有对象(blob,tree,commit,tag……),全部根据它们的内容生成SHA1哈希串值作为对象名;根据目前的数学知识,如果两块数据的SHA1哈希串值相等,那么我们就可以认为这两块数据是相同的。这样会带来的几个好处:
1,Git只要比较对象名,就可以很快的判断两个对象的内容是否相同。
2,因为在每个仓库(repository)的“对象名”的计算方法都完全一样,如果同样的内容存在两个不同的仓库中,就会存在相同的“对象名”。
3,Git还可以通过检查对象内容的SHA1的哈希值和“对象名”是否匹配,来判断对象内容是否正确。
4,根据上面的原则:Git确实根据内容来生成名字的,而且同名(SHA1哈希串值)肯定会有相同内容,但是提交对象(commit)和其它对象有点不一样,它里面会多一个时间戳(timestamp),所以在不同的时间生成的提交对象,即使内容完全一样其名字也不会相同。
1300697913 +0800就是时间戳,refs/heads/master对应的就是commit对象
(1)git add:添加至暂存区,但并未提交至服务器。git add . 是表示把当前目录下的所有更新添加至暂存区。有时在终端操作这个会提示:
warning: CRLF will be replaced by LF in GeneSmartStay/res/values-zh-rTW/strings.xml.The file will have its original line endings in your working directory.
这是因为文件中换行符的差别导致的。这个提示的意思是说:会把windows格式(CRLF)转换成Unix格式(LF),这些是转换文件格式的警告,不影响使用。
需要注意的是:如果在上次执行“git add”之后再对文件的内容进行了修改,那么在执行“git status”命令时,Git会对文件内容进行SHA1哈希运算就会发现文件又被修改了,仍会提示需要add,这是因为SVN是追加式的,只需要add一次;而GIT每次修改后的提交都是新文件,所以需要重新add。其实这时“readme.txt“就同时呈现了两个状态:被修改但未被暂存的文件(changed but not updated),已暂存可以被提交的文件(changes to be committed)。如果我们这时提交的话,就是只会提交第一次“git add"所以暂存的文件内容;只有再add后再提交,才是commit修改后的内容。
我们用一个实验来证明git的每次commit是独立的文件,不是追加的增量。我们在readme.txt复制后,在新文件的末尾增加了一行语句,变成了readme2.txt。查看两次提交的信息并cat文件内容
readme2.txt仅仅是末尾加了一行,但是可以看到不同commit对象对应的仍是完整的内容,而不是差异。
(2)git branch:可以创建一个新的分支,也可以查看当前仓库里所有的分支。git branch test创建名为test的分支,可以用git checkout test切换到test分支工作。如果仓库下的某分支不用了,可以用“git branch -d”命令把这个分支删掉。如果你想要删除的分支还没有被合并到其它分支中去,那么就不能用“git branch -d”来删除它,需要改用“git branch -D”来强制删除。
(3)git checkout:从暂存区恢复文件到工作区git checkout --
另一个较重要的功能类似于svn update,如果某次之前的commit有多个不同目录下的文件,现在只想恢复其中的某个文件到历史版本,用git checkout commit_id file_name //取文件file_name的 在commit_id是的版本,commit_id为 git commit 时的sha值。
(4)git clean:清除工作区未跟踪文件或者手工文件。
(5)git cat-file:查看节点文件,-p查看节点内容,-t查看节点类型。Git的数据对象包括:提交(commits)、树对象(trees)、二进制对象(blobs)、标签对象(tags)。这些对象之间是什么关系呢?
A,先用git log查看GIT本地创建的信息:
大家可以看到目前只有一个提交(commit)对象,而它的名字就是:”4f8d94496150661c48cb9fa119869702be1f3af6”。这个名字就是对象内容的一个签名串值,只要对象里面的内容不同,那么我们就可以认为对象的名字不会相同,反之也成立。为了方便表示,在不影响表达的情况下,一般只写SHA串值的前6个字符。
B,用 git cat-file -p查看节点信息,如下:
大家可以看到:提交“4f8d94” 是引用一个名为“5494546f6903153c9b16a81a6d9000debd275d31”的树对象(tree)。一个树对象(tree)可以引用一个或多个二进制对象(blob), 每个二进制对象都对应一个文件。 更进一步, 树对象也可以引用其他的树对象,从而构成一个目录层次结构。我们再看一下这个树对象(tree)里面有什么东东:
可以它直接引用alps我们代码的根目录。所有CODE就是根据这个来联系起来的。
C,我们可以用这个命令完成另一个实验,即相同内容的文件具有相同的哈希值,建跟readme.txt内容完全相同的另一个文件。执行:
这个命令是查看当前的提交(HEAD)所包含的blob对象,可见,它们的对象名完全一样。
(6)git commit:本地提交。git commit -m 'xxxxx',以添加LOG的形式提交更改。提交的是暂存区的内容,而不是工作区的内容。
(7)git config:查询和修改配置。当使用git时反馈*** Please tell me who you are,说明个人账户没有配置。git的配置文件;一共有三个:
A,/etc/gitconfig: 所有用户普遍适用的、系统的,git config --system选项
B,~/.gitconfig: 只适用该用户, git config --global选项
C,.git/config当前项目.git目录中,只适用当前项目, .git/config
每一层都覆盖上一层,也就是优先级 .gitconfig > ~/.gitconfig > /etc/gitconfig。配置内容:A,用户信息:包括个人用户名称和电子邮件名称;B,文本编辑器:设置在需要打开文本编辑器来输入某些信息时默认编辑器,例如vi、 vim、 emacs等;C,差异分析工具:在解决合并冲突时使用哪种差异分析工具。比如vimdiff;
查看配置内容:git config --list, 有时候会看到重复的变量名,那就说明它们来自不同的配置文件。不过最终Git 实际采用的是最后一个。
(8)git diff:本地文件与暂存区差异比较。查看本地文件和仓库已提交内容的差异:git diff HEAD;暂存区与HEAD的对比:git diff --cached;查看某个文件与仓库的差别:git diff filename.c 。查看master分支与子分支的差异:git diff 分支名。
(9)git fetch:获取远程版本库
(10)git init 和git clone:建立一个git仓库。有两种方式可以建立git仓库,一个是新建,一个是克隆现有的git仓库。 新建一个git仓库: git init;克隆现有的git仓库: git clone [url] [目录名(如果木有,则建立一个和原仓库同名的目录)。建立起来的git仓库都是在目录下多了一个.git目录,不同点是init的里边啥也木有,clone的是把目标git仓库复制过来了。Android GIT源目录https://android.googlesource.com/?format=HTML
一个例子来说明git clone。如果想看一下最新的Linux内核源代码,当我们打开它的网站时,发现有如下面的一段提示:
三行字符串表示三个地址,我们可以通过这三个地址得到同样的一份Linux内核源代码,即用git clone上面的任一行得到的是同样的内容。
我们先来看一下URL,git://、http://、https://这些代表是传输git仓库的协议形式,而“git.kernel.org“则代表了Git仓库存储的服务器名字(域名),“/pub/scm/linux/kernel/git/torvalds/linux-2.6.git” 则代表了Git仓库在服务器上位置。Git 仓库除了可以通过上面的git、http、https协议传输外还可以通过ssh、ftp(s)、rsync等协议来传输。git clone的本质就是把“Git目录”里面的内容拷贝过来,执行git clone的结果如下:
initialized这行意味着我们在本地先建了一个“linux-2.6”目录,然后在这个目录建了一个空的Git本地仓库(Git目录),里面将会存储从网上拉下来的历史提交。remote这两行代表服务器现在调用 git-pack-objects 对它的仓库进行打包和压缩。receving这行代表客户端接收服务器端发过送过来的数据。在我们执行完上面的clone linux-2.6代码的的操作后,Git会从“Git目录”里把最新的代码到签出(checkout)到“linux-2.6”这个目录里面。
(11)git log:显示提交日志(如果是clone来的,它也会继承所克隆的仓库的信息)。带参数-- fileName,fileName为任意文件名,查看指定文件的提交信息。 带参数 file/ , 查看file文件夹下所有文件的提交记录。git log .:当前目录下的提交记录。
另外,git log -n 1:查看最后一次提交的信息;git log -n 1 --stat:想看到最近一次提交所有更改过的文件;git log -n 1 -p:想看到最近一次提交所有更改的细节。git log -p xxx.c:查看某文件所有次提交的详细修改细节。
如果log跟tag都有使用,可以用git log tag_relold..tag_relnew来显示这两次tag之间所列出的commit Log记录。
(12)git merge:分支合并。git merge子分支名,把子分支与master合并。
“Updating b765df9..7f3c997”表示现在正在更新合并“b765df9”和“7f3c997”两个提交(commit)之间的内容;“b765df9”代表着主分支(master),“7f3c997”代表测试分支(test)。“Fast-forward”在这里可以理解为顺利合并,没有冲突。
如果修改的内容有冲突,则会有如下的提示:
合并命令的执行结果不是“Fast-foward”,而是“CONFLICT”。是的,两个分支的内容有差异,致使它们不能自动合并(Auto-merging)。先看一下工作目录的状态:
提示当前有一个文件“readme.txt”没有被合并,原因是“both modified”。再看一下暂存区里的内容:
“aac629”是当前主分支的内容,“034a81”是测试分支里的内容,而“4b5fa6”是它们共同父对象(Parent)里的内容。所以我们用编辑器合并编辑了工作区的内容后,在add 、commit,这个冲突就算解决了。怎么用“git add”添加到暂存区去,“git add”不是用来未暂存文件的吧,怎么又来解决冲突了?因为Git是一个“snapshot”存储系统,所有新增加的内容都是直接存储的,而不是和老版本作一个比较后存储新旧版本间的差异。
(13)git mv:重命名
(14)git pull:拉回远程版本库的更新,也即把服务器上的最新更改变更更新到本地工作区上( 比如git-pull git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git,即拉取源代码树的更新)。如果pull时提示诸如 fc7cc33..ebf8026 master -> origin/master,说明mastar已从fc7cc33更新到了ebf8026。本地如果想恢复到fc7cc33版本,再重新pull即可,要先恢复参见git reset。
(15)git push:推送至远程版本库
(16)git remote:远程版本库管理
(17)git rm:删除文件。git rm --cached
A,git rm file1 //删除文件跟踪并且删除仓库中的文件file1
git commit //提交刚才的删除动作,之后git不再管理该文件。
B,git rm --cached file1 //删除文件跟踪但不删除仓库的文件file1
git commit //提交刚才的删除动作,之后git不再管理该文件。但是文件系统中还是有file1。
不再跟踪某个目录,用git rm -rf xxx/(可能需要先保存好本地文件,避免误删),之后再commit。
(18)git status:显示整个工作区文件状态,该命令无法获知服务器上是否有更新,只能检测当前目录是否有未提交的。git status .:当前目录的差异状态。
(19)git reset:重置改变分支“游标”指向,或者从数据仓库恢复文件到工作区。git reset HEAD filename 取消暂存,文件回到已修改未暂存的状态。用git reset --hard 哈希码 ,可以恢复到之前旧的某个版本,比如:git reset --hard fc7cc33,则终端回显HEAD is now at fc7cc33 Support AccessibilityService。
(20)git sp:生成差分升级包。用git lg列出LOG记录后,用sp 哈希码旧..哈希码新,就可以生成一个两个版本间的区别差分包XXX.PATCH。
(21)git apply:在收到别人sp出给的patch,合并到自己的CODE中时,可以用命令来做这份工作。在master目录下git apply --reject xxx.patch就行了,不必手工来做。如果自己在合并patch之前做了其他修改,该命令会执行失败,需要手工合并。
(22)git ls-files --stage:查看暂存区内容。
你会发现“git目录“里多出了”.git/objects/2d/832d9044c698081e59c322d5a2a459da546469”这么一个文件,再执行“git cat-file -p 2d832d” 的话,就可以看到里面的内容正是“hello,world"。Git在把一个文件添加暂存区时,不但把它在索引文件(.git/index)里挂了号,而且把它的内容先保存到了“git目录“里面去了。
(23)git show:显示某次commit提交的细节和差异。比如git show commit_id,如果只看这次提交的某个文件的细节和差异,后面再加上路径和文加名就行了。
(24)git tag:可以用 git tag创建一个标签(tag)指定某个提交(commit)。比如 git tag stable-1 1b2e1d63ff,这样我们可以用stable-1 作为提交(commit) "1b2e1d63ff" 的代称(refer)。也即tag是某些典型log的另一个名称。用git tag -l可以列出所有的tag记录。
========================================================================================
假如项目在路经/home/ryan/codes/下面: git init
git add *
git commit -m "my project"
即可。需要注意的是,子目录需没有被GIT管理过,不然会遗留.git目录,导致最上层的GIT添加过程不会完整。
==========================================================================================
(1)当执行GIT操作时,如果回显:fatal: Unable to create ‘xxxx/.git/index.lock': File exists.If no other git process is currently running, this probably means a
git process crashed in this repository earlier. Make sure no other git process is running and remove the file manually to continue.
解决方法:rm -f ./.git/index.lock
===========================================================================================
使用GIT管理代码仓库时,有些文件是不需要进行管理的(不需要老被提示Untracked files,因为根本不用关注),比如编译生成的.o .a文件、某些生成文件夹等,这个时候就需要用到ignore文件。ignore文件均为如下格式:
# 忽略*.o和*.a文件
*.[oa]
# 忽略*.b和*.B文件,my.b除外
*.[bB]
!my.b
# 忽略dbg文件和dbg目录
dbg
# 只忽略dbg目录,不忽略dbg文件
dbg/
# 只忽略dbg文件,不忽略dbg目录
dbg
!dbg/
# 只忽略当前目录下的dbg文件和目录,子目录的dbg不在忽略范围内
/dbg
需要注意的是.gitignore文件放置位置,如果是对alps执行了git add/git commit,那么.git目录跟alps是同一层目录的。在当前目录下新建.gitignore文件,如果需要忽略alps/out目录,则添加内容out/即可,也即设定的当前所在目录是alps/。