Git 是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。
Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
Git 与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持
Git 不仅仅是个版本控制系统,它也是个内容管理系统(CMS),工作管理系统等。
如果你是一个具有使用 SVN 背景的人,你需要做一定的思想转换,来适应 Git 提供的一些概念和特征。
Git 与 SVN 区别点:
1、Git 是分布式的,SVN 不是:这是 Git 和其它非分布式的版本控制系统,例如 SVN,CVS 等,最核心的区别。
2、Git 把内容按元数据方式存储,而 SVN 是按文件:所有的资源控制系统都是把文件的元信息隐藏在一个类似 .svn、.cvs 等文件夹里。
3、Git 分支和 SVN 的分支不同:分支在 SVN 中一点都不特别,其实它就是版本库中的另外一个目录。
4、Git 没有一个全局的版本号,而 SVN 有:目前为止这是跟 SVN 相比 Git 缺少的最大的一个特征。
5、Git 的内容完整性要优于 SVN:Git 的内容存储使用的是 SHA-1 哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。
我们先来理解下 Git 工作区、暂存区和版本库概念:
工作区、版本库中的暂存区和版本库之间的关系如下
基本概念
工作区:workspace。就是你在电脑里能看到的目录。暂存区:英文叫 stage 或 index。一般存放在 .git 目录下的 index 文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。git add 添加到暂存区。版本库:工作区有一个隐藏目录 .git,这个不算工作区,而是 Git 的版本库。版本库包含暂存区。master:标记为 "master" 的是 master 分支所代表的目录树。 图中左侧为工作区,右侧为版本库。在版本库中标记为 "index" 的区域是暂存区(stage/index),标记为 "master" 的是 master 分支所代表的目录树。图中我们可以看出此时 "HEAD" 实际是指向 master 分支的一个"游标"。所以图示的命令中出现 HEAD 的地方可以用 master 来替换。图中的 objects 标识的区域为 Git 的对象库,实际位于 ".git/objects" 目录下,里面包含了创建的各种对象及内容。当对工作区修改(或新增)的文件执行 git add 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。当执行 git reset HEAD 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响。当执行 git rm --cached <file> 命令时,会直接从暂存区删除文件,工作区则不做出改变。当执行 git checkout . 或者 git checkout -- <file> 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。当执行 git checkout HEAD . 或者 git checkout HEAD <file> 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。git文件提交过程:
local reository 本地分支,git commit 添加到本地分支remote repository 远程分支,git push 推送到远程分支Git 常用的是以下 6 个命令:git clone、git push、git add 、git commit、git checkout、git pull,后面我们会详细介绍。
git init 命令用来初始化一个 Git 仓库,Git 的很多命令都需要在 Git 的仓库中运行。Git 仓库会生成一个 .git 目录,该目录包含了资源的所有元数据,其他的项目目录保持不变
git config --list 查看所有的属性
提交代码到暂存区(Index)
可以用它开始跟踪新文件,并放到暂存区,新建的文件尚未加到git的追踪中,需要使用git add 文件名来追踪或者把已跟踪的被修改的文件放到暂存区 //一个 git add name //所有 git add .提交代码到本地分支(local reository)
git commit -m "注释" #一次提交多个文件git commit name -m "注释" #一次提交一个文件 $ git commit -m "提交readme文件" [master (root-commit) eb8683b] 提交readme文件 1 file changed, 3 insertions(+) create mode 100644 README会返回当前是在哪个分支(master)提交的
Git 提供了一个跳过使用暂存区域的方式,只要在提交的时候,给 git commit 加上 -a 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤
$ git commit -a -m "不add直接提交" [master 60cc9af] 不add直接提交 1 file changed, 2 insertions(+), 1 deletion(-)#状态一 #On branch master #nothing to commit, working directory clean #所有已跟踪文件在上次提交后都未被更改过
#状态二 #Untracked files #新文件,还未被跟踪, 需要git add
#状态三 #Changes to be committed #文件已被跟踪,并处于暂存状态 已经git add 还没git commit
#状态四 #Changes not staged for commit #说明已跟踪文件的内容发生了变化,但还没有放到暂存,需要git add
比较文件的不同,即暂存区和工作区的差异。
执行 git diff 来查看执行 git status 的结果的详细信息。
git diff 比价暂存区和工作区的差异git diff --cached 比较暂存区和HEAD的差异git diff HEAD 比较工作区和HEAD的差异1.尚未写入暂存区的改动(即AM):git diff
2.查看已写入暂存区的改动: git diff --cached
3.查看已写入暂存区的与未写入暂存区的所有改动:git diff HEAD
3.显示摘要而非整个 diff:git diff --stat
不带参数的调用
git diff filename 这种是比较 工作区和暂存区比较暂存区与最新本地版本库
git diff --cached filename比较工作区和最新版本
git diff HEAD filename比较工作区与指定的 commit -id 的差异
git diff commit-id filename比较暂存区与指定 commit-id 的差异
git diff --cached commit-id filename比较两个 commit -id 的差异
git diff commit-id commit-idgit log -显示从最近到最远的提交日志,主要用于回退版本
git blame <file> -以列表形式查看指定文件的历史修改记录。
git log 查看日志
如果嫌输出信息太多,看得眼花缭乱的,可以试试加上--pretty=oneline参数或者加 --oneline
git log --pretty=oneline
--graph 查看历史中什么时候出现了分支、合并。以下为相同的命令,开启了拓扑图选项
git log --graph
--reverse 逆向显示所有日志
git log --reverse
git log --author 查找指定用户的提交日志可以使用命令:例如,比方说我们要找 Git 源码中 guxilong提交的部分,只要最近5条
git log --author=guxilong --oneline -5
--since 和 --before,指定开始结束日期,但是你也可以用 --until 和 --after。
例如,如果我要看 Git 项目中三周前且在四月十八日之后的所有提交,我可以执行这个(我还用了 --no-merges 选项以隐藏合并提交):
如果要查看指定文件的修改记录可以使用 git blame 命令
git blame <file>在Git中,用HEAD表示当前版本,也就是最新的提交1094adb...(注意我的提交ID和你的肯定不一样),上一个版本就是HEAD^,上上一个版本就是HEAD^^,当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100。
从暂存区回滚 (一定要看,回滚之前请保存或者提交代码。切勿在回滚的时候有未提交到分支的代码,会发生不可逆转的损失)
git checkout -- readme.txt意思就是,把readme.txt文件在工作区的修改全部撤销,这里有两种情况:
一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。总之,就是让这个文件回到最近一次git commit或git add时的状态。
回滚到指定版本号
git回退到上个版本
git reset --hard HEAD^回退到前3次提交之前,以此类推,回退到n次提交之前
git reset --hard HEAD~3回滚到指定的版本号
git log 找到指定的commit号git reset --hard commit号 本地仓库和暂存区都回滚到commit号提交版本号没必要写全,前几位就可以了,Git会自动去找
Git提供了一个命令git reflog用来记录你的每一次命令: git reflog
要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。可以用 git rm 命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了
如果只是简单地从工作目录中手工删除文件,运行 git status 时就会在 “Changes not staged for commit” 部分(也就是未暂存清单),然后再次提交
git rm --cached FileName另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。换句话说,仅是从跟踪清单中删除。比如一些大型日志文件或者一堆 .a 编译文件,不小心纳入仓库后,要移除跟踪但不删除文件,以便稍后在 .gitignore 文件中补上,用 --cached 选项即可
git checkout -- test.txt如果删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本。
git checkout其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。
注意:从来没有被添加到版本库就被删除的文件,是无法恢复的!
git mv 命令用于移动或重命名一个文件、目录或软连接。
git mv [file] [newfile]如果新但文件名已经存在,但还是要重命名它,可以使用 -f 参数:
git mv -f [file] [newfile]我们可以添加一个 README 文件(如果没有的话):
$ git add README然后对其重命名:
$ git mv README README.md $ ls README.md第一行告诉 Git 忽略所有以 .o 或 .a 结尾的文件。一般这类对象文件和存档文件都是编译过程中出现的,我们用不着跟踪它们的版本。第二行告诉 Git 忽略所有以波浪符(~)结尾的文件,许多文本编辑软件(比如 Emacs)都用这样的文件名保存副本。此外,你可能还需要忽略 log,tmp 或者 pid 目录,以及自动生成的文档等等。要养成一开始就设置好 .gitignore 文件的习惯,以免将来误提交这类无用的文件。
文件 .gitignore 的格式规范如下:
所有空行或者以注释符号 # 开头的行都会被 Git 忽略。可以使用标准的 glob 模式匹配。匹配模式最后跟反斜杠(/)说明要忽略的是目录。要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。星号(*)匹配零个或多个任意字符;[abc]匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)
我们再看一个 .gitignore 文件的例子:
# 此为注释 – 将被 Git 忽略 # 忽略所有 .a 结尾的文件 *.a # 但 lib.a 除外 !lib.a # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO /TODO # 忽略 build/ 目录下的所有文件 build/ # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt doc/*.txt # 忽略 doc/ 目录下所有扩展名为 txt 的文件 doc/**/*.txt**\通配符从 Git 版本 1.8.2 以上已经可以使用。
由于你的本地 Git 仓库和 GitHub 仓库之间的传输是通过SSH加密的,所以我们需要配置验证信息:
使用以下命令生成 SSH Key:
$ ssh-keygen -t rsa -C "自己的邮箱"后面的 自己的邮箱 改为你在 Github 上注册的邮箱,之后会要求确认路径和输入密码,我们这使用默认的一路回车就行。
成功的话会在 ~/ 下生成 .ssh 文件夹,进去,打开 id_rsa.pub,复制里面的 key。
回到 github 上,进入 Account => Settings(账户配置)。
左边选择 SSH and GPG keys,然后点击 New SSH key 按钮,title 设置标题,可以随便填,粘贴在你电脑上生成的 key。
添加成功后界面如下所示
为了验证是否成功,输入以下命令:
拉取项目,git pull origin master = git fetch origin + git merge origin/master
git clone 克隆项目
git clone git://github.com/schacon/simplegit-progit.git使用 git clone 从现有 Git 仓库中拷贝项目。行该命令后,会在当前目录下创建一个名为simplegit-progit的目录,其中包含一个 .git 的目录,用于保存下载下来的所有版本记录
克隆仓库的命令格式为:
git clone <url>如果我们需要克隆到指定的目录,可以使用以下命令格式:
git clone <url> <file> 参数说明: url:Git 仓库。 file:本地目录。添加远程仓库地址
git remote add origin url
查看当前的远程库
git remote
git remote -v
要查看当前配置有哪些远程仓库,可以用 git remote 命令,它会列出每个远程库的简短名字。在克隆完某个项目后,至少可以看到一个名为 origin 的远程库,Git 默认使用这个名字来标识你所克隆的原始仓库:
也可以加上 -v 选项(译注:此为 --verbose 的简写,取首字母),显示对应的克隆地址:
gu_xi@PC-GUXILONG MINGW64 /d/software/projectspace/ticgit (master) $ git remote origin gu_xi@PC-GUXILONG MINGW64 /d/software/projectspace/ticgit (master) $ git remote -v origin git://github.com/schacon/ticgit.git (fetch) origin git://github.com/schacon/ticgit.git (push)git remote show [remote-name]
我们可以也通过命令 git remote show [remote-name] 查看某个远程仓库的详细信息,比如要看所克隆的 origin 仓库,可以运行:
$ git remote show origin * remote origin URL: git://github.com/schacon/ticgit.git Remote branch merged with 'git pull' while on branch master master Tracked remote branches master ticgit除了对应的克隆地址外,它还给出了许多额外的信息。它友善地告诉你如果是在 master 分支,就可以用 git pull 命令抓取数据合并到本地。另外还列出了所有处于跟踪状态中的远端分支。
上面的例子非常简单,而随着使用 Git 的深入,git remote show 给出的信息可能会像这样:
$ git remote show origin * remote origin URL: git@github.com:defunkt/github.git Remote branch merged with 'git pull' while on branch issues issues Remote branch merged with 'git pull' while on branch master master New remote branches (next fetch will store in remotes/origin) caching Stale tracking branches (use 'git remote prune') libwalker walker2 Tracked remote branches acl apiv2 dashboard2 issues master postgres Local branch pushed with 'git push' master:master远程仓库的删除和重命名
在新版 Git 中可以用 git remote rename 命令修改某个远程仓库在本地的简称,比如想把 pb 改成 paul,可以这么运行:
$ git remote rename pb paul $ git remote origin paul注意,对远程仓库的重命名,也会使对应的分支名称发生变化,原来的 pb/master 分支现在成了 paul/master。
碰到远端仓库服务器迁移,或者原来的克隆镜像不再使用,又或者某个参与者不再贡献代码,那么需要移除对应的远端仓库,可以运行 git remote rm 命令:
$ git remote rm paul $ git remote origin只有在所克隆的服务器上有写权限,或者同一时刻没有其他人在推数据,这条命令才会如期完成任务。如果在你推数据前,已经有其他人推送了若干更新,那你的推送操作就会被驳回。你必须先把他们的更新抓取到本地,合并到自己的项目中,然后才可以再次推送。
git push -u origin master -u的意思是默认把本地master和远程的master关联上去,下次就直接git push
git fetch不会进行合并执行后需要手动执行git merge合并分支,而git pull拉取远程分之后直接与本地分支进行合并
git pull <远程主机名> <远程分支名>:<本地分支名>例如执行下面语句:
git pull origin master:brantest 将远程主机origin的master分支拉取过来,与本地的brantest分支合并git pull origin mastes
表示将远程origin主机的master分支拉取过来和本地的当前分支进行合并。
gu_xi@PC-GUXILONG MINGW64 /d/software/projectspace/e3mall (master) $ git fetch From https://github.com/guxilong/e3mall 43e1185..fb72f7f master -> origin/master gu_xi@PC-GUXILONG MINGW64 /d/software/projectspace/e3mall (master) $ git merge Updating 43e1185..fb72f7f Fast-forward README.md | 1 + test1.txt | 1 + 2 files changed, 2 insertions(+) create mode 100644 test1.txt注意在创建好分支后再切换分支的时候其实已经把主分支的内容已经到了支分支(这里是dev分支)里面了。在此分支上工作完后,需要切回master分支才能合并到master分支。
