• 1. GIT基础教程庞志强 2011.12.26
  • 2. 1、初识Git Git是一款分布式版本控制系统,有别于CVS和SVN等集中式版本控制系统,Git可以让研发团队更加高效的协同工作,从而提高生产率。使用Git,开发人员的工作不会因为贫乏的遭遇提交冲突而中断,管理人员也无需为数据备份而担心。经过Linux这样庞大的项目考研之后,Git被证明可以胜任任何规模的团队。
  • 3. 2、Git初始化Git的初始化 首先通过下面命令查看git版本 #git --version 在开始使用Git之前,我们首先要用git config命令设置一下git的配置变量,主要有以下几步: (1)配置姓名,这个将在提交的时候用到 #git config --global user.name “pang” #git config --global user.email pang@126.com (2)设置一些别名,以便使用更为简洁的子命令 #git config --global alias.ci commit (3)开启颜色显示 #git config --global color.ui true
  • 4. 创建版本库及第一次提交 首先建立一个新的工作目录,并在这个目录下建立版本库 #cd /path/to/my/workspace #mkdir demo #cd demo #git init Initialized empty Git repository in /path/to/../demo/.git 从上面初始化的输出信息来看,工作区创建了隐藏目录.git #ls –aF ./ ../ .git/ 这个隐藏的.git目录就是Git版本库 下面向工作区添加文件 #echo “Hello.”>welcome.txt 将这个文件添加到版本库 #git add welcome.txt 这里还没有完,需要提交一次才能进入版本库 #git commit –m “initialized” 提交必须有提交说明,-m参数可以直接给出提交说明
  • 5. 为什么会有.git目录 在非工作区执行git命令会因为找不到.git目录而报错 #cd /path/to/my/workspace #git status 在工作区建立a/b/c目录并进入 #mkdir –p a/b/c #cd a/b/c 显示版本库目录 #git rev-parse --git-dir 显示工作区根目录 #git rev-parse --show-toplevel 显示工作区间根目录的相对目录 #git rev-parse --show-prefix 显示当前目录到工作区的深度 #git rev-parse --show-cdup
  • 6. git config命令的参数区别 执行下面命令,将打开.git/config文件进行编辑 #git config -e 执行下面命令,将打开/home/git/.gitconfig文件进行编辑 #git config -e --global 执行下面命令,将打开/etc/gitconfig系统级配置文件进行编辑 #git config -e --system 以上三个配置文件分别是Git版本库级别的配置文件、全局配置文件(用户主目录下)和系统级配置文件(/etc目录下)。其优先级别依次降低。
  • 7. 谁在提交? 在使用Git之前我们设置了全局变量user.name,如果不设置会出现什么后果呢 执行下面命令,删除全局变量中的user.name和user.email #git config --unset --global user.name #git config --unset --global user.email 这样一来,关于用户的设置就被清空了,尝试一下提交 #git commit --allow-empty -m “who does commit?” 由于没有设置用户,会给出一段警告。查看下提交记录 #git log 可以看出Git对于用户姓名进行了大胆猜测,猜测用户为当前终 端登录用户。 为了保证提交者信息的准确性,需要对提交恢复用户设置 #git config --global user.name “pang” #git config --global user.email pang@126.com #git commit --amend --allow-empty --reset-author 其中--amend参数表示是修补提交,对上一次提交进行修补,而不会产生新的提交。
  • 8. 小结了解了Git如何初始化版本库及进行提交 熟悉Git配置变量的设置
  • 9. 3、Git暂存区修改能直接提交吗? 首先更改welcome.txt文件,在文件后面追加一行。 #echo “Nice to meet you.”>>welcome.txt 比较本地与版本库中得差异 #git diff 可以看到文件修改了,那么提交 #git commit -m “Append a nice line.” 没有成功,查看提交日志,也没用新的记录 #git log #git status -s M welcom.txt 添加下修改文件 #git add welcome.txt #git diff
  • 10. 没有差别,难道是被提交了?在看一下当前状态: #git status -s M welcome.txt 两次状态输出有细微的差别,虽然都是M(modified)标示,但是位置不一样。git add执行前,M位于第二列,执行后位于第一列。 第一列表示版本库与暂存区的比较,第二列表示工作区与暂存区的区别。通过下面命令进一步体会: #echo “Bye bye.”>>welcome.txt #git status -s MM welcome.txt #git commit -m “which wersion checked in?” #git status -s M welcome.txt 保存下我们的工作,后面的进度恢复会用到。 #git stash
  • 11. 理解Git暂存区理解暂存区工作区版库 本暂存区masterobjectsaddcheckoutcommitresetHEAD
  • 12. 图中左侧是工作区,右侧是版本库。版本库包括暂存区,master分支,对象库等。 HEAD实际上是指向分支的一个游标。 图中objects标示的区域是Git的对象库。 当对工作区的修改的文件执行git add命令时,暂存区的目录会被更新,同时工作区的文件会被写入到对象库中。 当执行提交(git commit)时,master的目录树会根据暂存区做出相应的更新。 当执行git reset HEAD命令时,暂存区的目录树会被master分支的目录树所替换。 当执行git checkout .命令时,会用暂存区的文件置换工作区的文件。(危险) 当执行git checkout HEAD .命令时,会用master分支的内容替换暂存区和工作区文件。(危险)
  • 13. 目录浏览 查看HEAD目录树 #git ls-tree --l HEAD 浏览暂存区目录树,先清除工作区改动 #git clean -fd #git checkout . 对工作区做以下修改 #echo “Bye-bye.”>>welcome.txt #mikdir -p a/b/c #echo “Hello.”>a/b/c/hello.txt #git add . #echo “Bye-bye.”>>a/b/chello.txt #git status -s #find . -path ./.git -prune -o -type -printf “%-20p\t%s\n”
  • 14. Git Diff 魔法Git Diff工作区版库 本暂存区masterobjectsGit diffgit diff --cachedHEADgit diff HEAD
  • 15. 小结了解Git的工作原理。 了解status的用法。 知道工作区,暂存区之间的区别。 学会diff不同的用法。
  • 16. 4、Git的对象 Git里见得最多的就是40位16进制的ID号了,产看最近一次提交 #git log -1 --pretty=raw 通过提交日志,我们发现一个提交里含有3个ID: commit:本次提交的唯一标示 tree: 本次提交所对应的目录树 parent: 本次提交的父提交 研究git对象ID的一个重要武器就是git cat-file命令 #git cat-file -t [ID] 查看ID类型 #git cat-file -p[ID] 查看ID内容 通过这个命令就可以对历史提交进行追踪了。
  • 17. 对象库 Id e6956 Tree f58d Parent a0c6commit Id a0c6 Tree 190d Parent 9e8acommit Id f58d Blob fd3c |_welcome.txt Tree Id fd3c Hello.Blob对象库
  • 18. ID为什么不用顺序数字 Git的提交为什么不用顺序数字而采用40位16进制,这是因为Git是一个分布式的版本控制系统。如果采用顺序递增的编号,只能保证本地版本库的唯一性,在分发的时候难免会造成冲突。所以采用40位ID可以保证编号的全球唯一。 40位的ID如何访问? 对于人来说,要记住40位的16进制数是很困难的,Git提供了很多方法可以方便的访问这些ID。 1、不必写全ID,只采用开头部分(一般4位以上),只有不与现有其他ID冲突即可。 2、使用HEAD代表最新提交,则 HEAD^表示HEAD的父提交 HEAD~5表示HEAD^^^^^
  • 19. 小结了解Git对象的概念 通过ID追踪历史提交
  • 20. 5、Git重置 我们知道,master相当于一个分支游标,每次都指向最新的提交。但是既然是游标,就应该既可以向上“游动”,也可以向下“游动”。 下面来体会下master游标是怎么变化的 #cat .git/refs/heads/master //查看master指向 #touch new-commit.txt #git add new-commit.txt #git commit -m ”does master change?” #cat .git/refs/heads/master 下面,我们重置下master游标 #git reset --hard HEAD^ #cat .git/refs/heads/master
  • 21. 可以看到,不仅刚才提交的文件没了,连提交日志中的记录也不见了。使用重置命令很危险,会彻底丢掉历史。那么,利用浏览提交历史的方法找到丢弃的ID,在使用重置恢复历史吗?不可能!因为重置让提交历史也改变了。 #git log 发现提交日志中被丢弃的提交已经不存在了。所以我们无法通过丢弃的ID来进行恢复。 那么,如果不小心进行了错误的重置,应该如何去挽救呢?
  • 22. 利用reflog挽救错误重置 如果没有记下被丢弃的提交ID,想要重置回原来的提交很麻烦。幸好Git提供了挽救机制。日志目录下有专门记录分支变更的文件。 查看最近5次变更记录。 #tail -5 .git/logs/refs/heads/master #git reflog show master | head -5 根据reflog显示,master@{2}是最后一次提交 #git reset master@{2} #git log 可以发现提交历史也都恢复了。
  • 23. 深入了解git reset命令#git reset [-q] [] [--] #git reset [--soft|--hard…] [-q] [] 上面两个用法,是可选项,省略则表示是HEAD指向的提交。 第一种用法包含路径,不会重置引用,也不会改变工作区,相当于取消了之前执行的 git add命令。 第二种则会重置引用,根据不同的选项可以对暂存区和工作区进行重置。 --hard:替换引用;置换暂存区;置换工作区。 --soft: 替换引用。
  • 24. 小结熟悉reset重置的用法。 学会利用reflog恢复错误重置。
  • 25. 6、Git检出 重置命令可以更改master的游标指向,如何能够改变HEAD的指向呢? HEAD可以理解为头指针,是当前工作区的“基础版本”,当执行提交的时候,HEAD所指向的提交将作为新提交的父提交。下面查看下当前HEAD的指向。 #cat .git/HEAD #git branch -v 用git checkout命令检出当前提交的父提交。 #git checkout [ID]^ 查看下现在HEAD和master所对应的提交ID。 #git rev-parse HEAD master 可以看出当前头指针和master已经指向了不同的提交。即当前是处于“分离头指针”状态。
  • 26. 现在再做一次提交,HEAD会怎么变化呢? #touch detached-commit.txt #git add detached-commit.txt #git commit -m “commit in detached HEAD mode.” #cat .git/HEAD #git log --graph --pretty=oneline 可以看到HEAD指向了最新的提交,并且是建立在之前指向的提交之上的。下面切换到master分支上。 #git checkout master 切换之后,HEAD重新指向了分支,而不是断头模式。但是刚才 提交的日志也不见了。 #git log --graph --pretty=noeline 挽救分离头指针,因为刚才的提交未被任何分支追踪到,因此不能保证这个提交会永远存在于对象库中。 #git merge [ID]
  • 27. 深入理解git checkout命令#git checkout [-q] [] [--] #git checkout [] #git checkout [-m] [[-b|--orphan] ] [] 第一种用法是可选项,如果省略则相当于从暂存区进行检出。和重置命令不同,重置一般用于重置暂存区,而检出主要是覆盖工作区。这种用法不会改变HEAD,主要用于指定版本的文件覆盖工作区的对应文件。 第二种用法则会改变头指针。只要HEAD切换到一个分支上才会被跟踪,否则就会处于分离头指针状态。第二种用法主要是用于切换分支的。 第三种用法主要是用来创建并切换到新的分支的。新的分支从指定的提交开始。
  • 28. 小结了解检出的作用 初步了解检出的用法
  • 29. 7、进度恢复 我们在暂存区一节保存了工作进度Git可以通过下面命令恢复工作进度。 #git stash list #git stash pop 保存的进度都恢复了,下面进行提交。 #git commit -m “add new files:a/b/chello.txt,but leave welcome.txt.” 后悔了,重置放弃最新的提交。 #git reset --soft HEAD^ 添加welcome.txt,撤出a/b/c/hello.txt #git add welcome.txt #git reset HEAD a/b/c/hello.txt
  • 30. 全部清除 #git reset #git checkout --welcome.txt #git clean -fd stash的用法: #echo Bye bye>>welcome.txt #echo hello.>hack-1.txt #git add hack-1.txt #git stash save “hacked welcome.txt,newfile hack-1.txt” 工作区恢复了原貌,修改都不见了。 #echo fix.>hack-2.txt #git stash 现在有了两个保存进度,如何恢复第一个进度 #git stash apply stash@{1} #git stash clear
  • 31. 小结学会stash的常用命令 stash save stash pop stash list stash apply stash clear
  • 32. 8、删除操作 前面讲的都是如何添加更改文件,下面是如何删除文件。首先保存下当前进度。 #git stash #git stash apply 查看下当前工作区都有哪些文件: #ls 直接在工作区删除文件 #rm *.txt 通过下面命令,可以看到版本库中文件还在 #git ls-files 结论:工作区删除文件对版本库无任何影响
  • 33. 执行git rm命令删除文件 根据上面的输出的提示,我们应该使用git rm命令来删除 #git rm detached-txt hack-1.txt new-commit.txt welcome.txt 删除动作被添加到了暂存区,这时候执行提交动作,就从真正意义上执行了文件删除。 #git commit -m “delete files using git rm.” 但是,文件只是在版本库的最新提交中删除了,在历史提交中还在。可以通过下面的命令查看历史版本的文件列表。 #git ls-files --with-tree=HEAD^ 也可以查看历史版本中删除的文件内容。 #git cat-file -p HEAD^:welcome.txt git add -u 快速删除 恢复下现场 #git reset --hart HEAD^ #git stash apply -q #rm *.txt #git add -u #git commit -m “delete files using git add -u.”
  • 34. 恢复删除的文件 经过上面的操作,工作区已经没有了任何的文件。上面说过执行删除并提交,只是在提交中删除了文件,在历史版本中还存在。我们可以通过历史版本来恢复文件。执行下面命令可以恢复welcome.txt文件。 #git cat-file -p HEAD^:welcome.txt>welcome.txt 或者 #git show HEAD^:welcome.txt>welcome.txt 当然,使用checkout命令更为简洁实用。(思考) 文件已经被恢复到文件区了,提交到版本库就可以了。 #git add -A #git commit -m “restore files.” 通过再次添加删除文件是最自然的恢复方法。其他版本库如CVS也采用同样的方法。但是其他系统却会带来严重的副作用--文件变更历史被人为割裂,或者是存储空间的浪费。Git却没有这种副作用。联想下Git对象库
  • 35. 小结学会删除文件的方法 学会恢复删除的方法
  • 36. 9、Git克隆鸡蛋不要装在一个篮子里 版本库A版本库B.git.gitgit clone①②A ①:PUSH ②:PULLB ①:PULL ②:PUSH
  • 37. git clone 克隆版本库主要有三种用法 #git clone #git clone --bare #git clone --mirror 方法1是将repository指向的版本库克隆到directory目录下。文件都会检出,版本库位于工作区下的.git目录中。 方法2和3创建的克隆版本库都不包含工作区,直接就是版本库的内容,这样的版本库称为裸版本库。一般约定俗成裸版本库的目录以.git为后缀,所以上面示例中将克隆出来的裸版本库目录名写作。 用法3区别于用法2之处在于用法3克隆出来的裸版本库对上游版本库进行了注册,这样可以在裸版本库中使用git fetch命令和上游版本库进行持续同步。
  • 38. 对等工作区 #git clone /path/../demo /path/../demo-backup #cd demo #git commit --allow-empty -m “test1” #git commit --allow-empty -m “test2” #git push /path/../demo-backup 出错了,因为默认更新非裸版本库的当前分支是不被允许的,这将会导致暂存区和工作区与推送至版本库的提交不一致。 #cd demo-backup #git pull #git log --oneline -2 为什么执行pull命令没有像push那么长的命令,因为在执行clone命令时,克隆出来的版本库对原版本库进行了注册。所以执行拉回的时候无需设置上游版本库的地址。
  • 39. 克隆裸版本库 #git clone --bare /path/../demo /path/../demo.git #cd demo #git commit --allow-empty -m “test3” #git commit --allow-empty -m “test4” #git push /path/../demo.git 这次推送成功了,因为裸版本库不存在工作区,因此不会造成当前分支与工作区的不同。
  • 40. 创建生成裸版本库 裸版本库不但可以通过克隆方式创建,还可以通过git init命令以初始化的方式创建,之后的同步与上面大同小异。 #git init --bare /path../demo-init.git #cd /path../demo #git push /path../demo-init.git 推送出错了,这是由于创建的是裸版本库,没有工作分支,所以无法推送。 #git push /path../demo-init.git master 加上参数master,表示推送到master分支上,推送成功。 #git commit --allow-empty -m “test5” #git commit --allow-empty -m “test6” #git push /path/../demo-init.git 经过刚才的推送,版本库中已经有分支了,所以推送的时候不需要再加上分支参数了。
  • 41. 小结了解三种克隆方法 熟悉推送与拉回操作
  • 42. Git工作协同Git支持的主要协议 协议名称语法说明SSH[user@]ip:URLSCP格式,更简洁GITgit://ip[:port]URL最常用的只读协议HTTPhttp://ip[:port]URL兼有智能协议和哑协议FTPftp://ip[:port]URL哑协议RSYNCrsync://ipURL哑协议本地协议File://URL
  • 43. 多用户本地模拟 通过本地协议,可以在一台电脑上模拟多人操作。首先建立一个共享的裸版本库。 #git init --bare /workspace/shared.git 用户1克隆版本库 #cd /user1/workspace #git clone file:///workspace/shared.git project 设置用户1 #cd project #git config user.name user1 #git config user.email user1@126.com 创建文件并提交 #echo Hello.>README #git add README #git commit -m “initial commit” #git push origin master
  • 44. 创建用户2 同样的我们可以通过克隆创建出用户2的版本库 #cd /user2/workspace #git clone file:///workspace/shared.git project 设置用户2 #cd project #git config user.name user2 #git config user.email user2@126.com 查看提交日志 #git log 可以看到有用户1的提交日志。
  • 45. SSH多用户协同 和本地模拟同步类似,但是我们需要做些前期工作。客户机需要能够无密码访问共享版本库服务器,具体步骤参考hadoop配置中ssh配置部分。克隆的时候需要将本地协议换为ssh协议,例如用户1克隆版本库如下: #cd /user1/workspace #git clone git@192.168.192.2/workspace/shared.git project
  • 46. 小结学会本地模拟多用户协同 练习多用户ssh协同
  • 47. 到这里,Git的基础知识就学完了,但是提交中的冲突要如何解决?分支将如何管理?敬请继续关注Git进阶篇 庞志强 2011-12-26