工作区:就是你在电脑里能看到的目录。
暂存区:英文叫stage, 或index。一般存放在 “.git目录下” 下的index文件(.git/index)中,所以我们把暂存区有时也叫作索引(index)。
版本库:工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系:
图中左侧为工作区,右侧为版本库。在版本库中标记为 “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 " 命令时,会直接从暂存区删除文件,工作区则不做出改变。
当执行 “git checkout .” 或者 "git checkout – " 命令时,会用暂存区全部或指定的文件替换工作区的文件。这个操作很危险,会清除工作区中未添加到暂存区的改动。
当执行 “git checkout HEAD .” 或者 "git checkout HEAD " 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令也是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。
一般工作流程如下:
克隆 Git 资源作为工作目录。
在克隆的资源上添加或修改文件。
如果其他人修改了,你可以更新资源。
在提交前查看修改。
提交修改。
在修改完成后,如果发现错误,可以撤回提交并再次修改并提交。
下图展示了 Git 的工作流程:
git init # 初始化git管理仓库,会出现.git文件夹
git clone ssh://git.com/cloud-codec-qa/cloud-code-diff-test.git
git clone ssh://git.com/cloud-codec-qa/cloud-code-diff-test.git new_name # 使用新的命名
设定user的name和mail信息
/etc/gitconfig,操作系统:
git config --system user.name "张三"
git config --system user.email "[email protected]"
git config --unset user.name
git config --unset user.email
~/.gitconfig,针对用户:
git config --global user.name "张三"
git config --global user.email "[email protected]"
git config --global alias.br branch # 别名设置
git config --global alias.ui '!gitk' # 感叹号表示需要
.git/config,针对特定项目:
git config --local user.name "张三"
git config --local user.email "[email protected]"
git config --list
*.a # 忽略所有.a结尾的文件
!lib.a # 但lib.a除外
/TODO # 仅仅忽略项目根目录下的TODO文件,不包含subdir/TODO
build/ # 忽略build/目录下的所有文件
doc/*.txt # 会忽略doc/notest.txt但不包含 doc/server/arch.txt
doc/*/*.txt
doc/**/*.txt
/*/test3.txt # 忽略一级目录下的test3.txt文件
/**/test3.txt # 忽略多级目录下的test3.txt文件
git add test.txt
git add . # 提交当前目录下的修改,如果.gitignore匹配,则不加入到暂存区
git add * # 不管.gitignore是否匹配,均增加到暂存区
git add --all # 提交所有修改到暂存区
git add foo.txt # 提交单个文件到暂存区
git add dir/ # 提交指定某一包含新增文件与被修改文件的目录
git commit -m 'second'
git commit -amend "正确的信息" # 与上一次是一次提交,只能修改最后一次提交
git commit -am 'my logging' # 等价于git add + git commit,不会提交新增文件
git rm --cached test.txt # 删除了一个文件,并且未被追踪,可以通过reset+checkout恢复
rm # linux命令删除,并未纳入stage区,只需要reset即可
git rm -f hello.txt # 工作区中的文件被修改后的删除方式
git mv test1.txt test2.txt # 等价于mv test1.txt test2.txt + git rm test1.txt + git add test2.txt
get checkout -- file.txt # 修改工作区文件与暂存区对齐
git reset HEAD test.txt # 将添加到暂存区的内容从暂存区移动到工作区
git help
git help config
git config --help
man git-config
git log -n 3 # 显示近三次的提交
git log --pretty=oneline 或 git log --oneline # 显示概述信息
git log --pretty=format:""
git log --format=fuller # 显示许多细节信息
git log -stat -l # 查看提交中包含的重要信息
git log -stat # 显示统计
git log --merges 查看连个父级提交
git log --graph # 日志图形化
git log --graph --abbrev-commit
git log --graph --pretty-oneline --abbrev-commit
git log origin/master # 三种写法等价,最后都转换成refs/remotes/origin/master执行
git log remotes/origin/master
git log refs/remotes/origin/master
git blame test.txt # 查看test.txt文件所有的修改
git status # 显示出该项目自上次提交以来所有发生的所有修改
git status --short # 相关输出更紧凑
diff 当前文件a 目标文件b # 列出当前文件a同目标文件b的区别
git diff # 比较暂存区与工作区的文件差异
git diff HEAD # 比较最新的提交与工作区之前的差别
git diff --cached # 比较最新的提交与暂存区的之间的差别
git diff --cached commit_id # 比较某次提交与暂存区之间的差别
git diff temp # 查看temp分支与本地原有分支的不同
git diff foo.txt 显示每个被修改的行
git diff --staged 显示当前版本库的HEAD提交与暂存区之间的不同(通过--staged显示暂存区内容)
git diff 77d231f HEAD 两次提交之间的比较;在不用提交散列值的情况下,靠指定相关特定的符号名称(例如分支、标签、HEAD等)也能够获取到
git diff 77231f^! 与上一次提交进行比较;使用^!
git diff 77231f 05bcfd1 - book/bisection 限制只显示某些文件或目录之间的差异
git diff --stat 77d231f 05cfd1 使用--stat选项显示每个文件中的修改数量
git diff --word-diff 按照单词显示我们所做的修改
git diff --word-diff=color 以不同的颜色来显示文件中的不同
拉取,同时会合并,等价于 pull == fetch + merge
git pull origin develop:develop
git push origin src:dest # 完整写法
git push origin :develop # 将本地空分支推动到远程仓库,则远程分支被删除
git push origin --delete develop # 同上一条命令,删除远程仓库分支
git push --set-sream origin develop # 在远程上创建与本地相同的分支
git push --set-sream origin develop:develop2 # 远程分支名字为develop2
git push origin HEAD:develop2 # 同上一条命令
git push -u origin test # 将本地分支推送到远程仓库并创建新的分支
git remote add origin http://github.com/myname/git_project.git # 添加远程仓库信息
git remote -v # 查看远程分支
git remote show # 查看远程分支名称
git remote show origin # 远程仓库的详细信息
git remote prune origin # 远程分支被删除之后,裁剪本地分支(stale状态)
git remote rename origin origin2 # 将远程仓库重命名
git remote rm origin # 删除远程仓库
git remote add origin [email protected]/git-test.git # 上一条删除后,重新加回来远程仓库
git config --global push.default simple
git fetch # 将远程仓库拉取到本地远程仓库
git fetch origin master:refs/remotes/origin/mymaster
git fetch origin master:refs/remotes/origin/mymaster topic:refs/remotes/origin/topic # 一次性拉取多个分支
删除远程分支:先删除远程分支,再从本地推送到远程分支
HEAD 指向当前分支,master指向提交
Fast-Foward master只是指向新的提交,并没有更改
git branch temp # 新建一个temp分支
git branch -v
git branch newcommit 1e4q5r #基于 1e4q5r分支建立名字未newcommit的branch
git branch -a # 查看本地分支和远程分支
git branch -av # 查看本地分支和远程分支,带上最后一次提交的节点信息
git branch --unset-upsream
git branch -d temp # 删除temp分支
git checkout new_name # 为某个提交起一个有意义的名字,这就是所谓的标记
git checkout - # 两个目录来回切换
git checkout -d new_name # 没有文件改动可直接删除,有改动的话提示不可删除
git checkout -D new_name # 无论有无改动,直接删除该分支
git checkout -b new_branch # 等价于创建分支并且切换到该分支
git checkout -b develop origin/develop # 将远程仓库develop的代码拉到本地远程分支
git checkout --track origin/test # 等价于上一条命令
git checkout . #本地所有修改的。没有的提交的,都返回到原来的状态
git merge new_branch # 将new_branch合并到当前分支
git merge --no-ff dev # 如果可能,合并分支时GIT会使用fast-forward模式,在这种情况下,删除分支会丢掉分支信息,合并时加上--no-ff参数会禁用fast-forward模式,这样会多出一个commit id
$ git checkout -b iss53
这相当于执行下面这两条命令:
$ git branch iss53
$ git checkout iss53
切换分支的时候最好保持一个清洁的工作区域。稍后会介绍几个绕过这种问题的办法(分别叫做 stashing 和 commit amending)。
分支:branch命令只能用于创建新的分支,但不会自动切换到新的分支,如果想切换用checkout
git branch 列出所有分支
git branch a-branch # 为当前提交创建分支
git branch still-a-branch 38b7da45e # 为任意一批提交创建分支
git branch still-a-branch older-branch # 从现有分支中创建分支
git branch -d b-branch # 删除一个已经被终止的分支
git branch -D b-branch # 删除打开的分支(删除一个分支时还未转移到其他分支)
恢复分支
git branch a-branch 742dcf6 # (已知提交散列值)恢复某个分支
git relog # 用reflog命令将想要恢复分支提交的散列值找到
git branch b-branch HEAD@{1} # (通过reflog命令找到散列值)恢复该分支
git checkout a-branch # 切换到a-branch分支
git chechkout -b a-branch # 创建并切换到新的分支a-branch,=创建+切换
git checkout --force a-branch # 强制切换
git stash & git checkout a-branch # 储存修改并切换
git reset --hard HEAD^ # 回到上一个提交
git reset --hard HEAD^^ # 回到前两个提交
git reset --hard HEAD~1
git reset --hard commit_id
git reflog # 操作日志
git reset HEAD test.txt # 将文件移除暂存区
git checkout commit_id # detached的信息
git stash # 将工作区和暂存区中修改保存在一个储存栈的缓存区
git stash list # 查看储存栈中的内容
git stash pop # 将最后一次stash内容弹出并删除该条记录
git stash apply # stash内容不删除,需通过git stash drop stash@{0}手动删除
git stash drop stash@{0} # 删除stash中的日志信息
git stash applay stash@{0} # 直接弹出某次的stash
(git标签与分支无关,一经建立则就在git中存在)
git tag # 查看所有标签
git tag v1.0 # 轻量级标签,不会创建tag对象
git tag -a v2.0 -m 'release version' # 带有附注标签,会创建tag对象,对象的内容指向commitID
git tag -d tag_name # 删除本地分支
git tag -l 'v1.0'
git tag -l 'v*'
git push origin v7.0 # 推送v7.0到远程,简写
git push origin refs/tags/v7.0:refs/tags/v7.0 # 完整写法,源:目的地
git push origin refs/tags/v7.0 # 将空的标签推送到远程,即删除
git push origin --delete tag v5.0 # 删除远程标签
git fetch origin tag v7.0 # 仅拉取7.0的标签
git checkout v1.0
在缺省的情况下,refspec会被git remote add命令自动生成,git会获取远端上的refs/heads下的所有引用,并将它们写到本地的refs/remotes/origin目录下,所以,远端上有一个master分支,在本地可以通过下面几种方式访问它们的历史记录:
git log origin/master # 三种写法等价,最后都转换成refs/remotes/origin/master执行
git log remotes/origin/master
git log refs/remotes/origin/master # 最完整的写法
标签有两个ID,一个是提交的ID,一个是他指向的commitID
将.git/refs中的信息打包成.git/pack-refs,新增加的分支会在.git/refs中,可以重新调用git gc再次加入到.git/pack-refs中
git init --bare # 创建个裸库,没有工作区的,存放开发者所存放的代码,一般在服务端
git仓库中存在依赖的git仓库
git submodule add [email protected]/gitlecture/git-child.git mymodule # mymodule事先不能存在,将整个git-child仓库的数据创建到mymodule中,同时生成.gitmodules
git submodule foreach git pull origin master
父项目和子项目一起克隆
git clone [email protected]/gitlecture/git-parent.git git_parent --recusive
--recusive等价于下面三句,同步clone git_child的git库
git clone 父项目
git submodule init
git submodule --update --recursive
删除子项目
git rm -cached mymodule # 从暂存区移除
rm -rf mymodule # 从工作区移除
rm -rf .gitmymodule # 移除配置文件
相同点:与git submodule作用同,官方推荐使用subtree;
不同点:1、submodule是一个子库的引用,而subtree是在父库里面创建子库所有文件,如果父项目对子项目有所更改,会推送到远端父项目的子项目,若此时没有同步父项目中子项目信息到子库,则子库不发生改变;
三种写法一样:
git subtree add --prefix=subtree
git subtree add --prefix subtree
git subtree add -P subtree
–squash,合并,将subtree多个提交(多个commitID)合并成一个提交(重新生成一个commitID),防止子仓库多个提交污染父仓库的提交历史,如果一开始使用 --squash参数就在后面一直使用,如果一开始不使用,则后面就一直别使用;否则会有其他奇怪的错误出现。
使用 --squash
git remote add subtree-origin [email protected]/gitlecture/git-child.git # 添加子库的远程信息
会产生两次提交,第一次是子仓库的多次commit合并成一个commit,第二次merge产生一个新commit,所以子库的更新都会生产两个commitID
git subtree add --prefix=subtree subtree-origin master --squash
git subtree pull --prefix=subtree subtree-origin master --squash
git subtree push --prefix=subtree subtree-origin master # 将本地的更改推送到远程子分支
git subtree push --prefix=subtree subtree-origin master -d # 显示其他的提示信息
不使用 --squash
git remote add subtree-origin [email protected]/gitlecture/git-child2.git # 所有的子仓库提交都会放到父仓库中,不会存在合并
git
git subtree pull --prefix=subtree subtree-origin master
git subtree push --prefix=subtree subtree-origin master
$ git subtree
usage: git subtree add --prefix=
or: git subtree add --prefix=
or: git subtree merge --prefix=
or: git subtree pull --prefix=
or: git subtree push --prefix=
or: git subtree split --prefix= # 将一个模块独立出去
-h, --help show the help
-q quiet
-d show debug messages
-P, --prefix ... the name of the subdir to split out
-m, --message ... use the given message as the commit message for the merge commit
1. List item
options for 'split'
--annotate ... add a prefix to commit message of new commits
-b, --branch ... create a new branch from the split subtree
--ignore-joins ignore prior --rejoin commits
--onto ... try connecting new tree to an existing one
--rejoin merge the new branch back into HEAD
options for 'add', 'merge', and 'pull'
--squash merge subtree changes as a single commit
把一个分支中的修改,应用到另外一个分支上;主要应用在本地分支中
git cherry-pick commitID # 跳过commitID之后会存在冲突,出现冲突后,手动解决;
develop恢复原始状态:
git checkout 第一次的commitID # 此时会有一个游离状态的分支出现
git delete -D develop # 删除原有develop分支
git checkout -d develop # 命名当前分支为develop
将一个分支修改应用到另外一个分支上,将分叉改为直线,rebase会修改提交历史
git checkout mywork
git rebase origin # 到test分支,rebase origin,将mywork上的节点当做补丁
git rebase --continue
git rebase --skip # 如果产生冲突,则会以origin的修改为主,丢弃mywork的补丁修改
git rebase --abort # rebase过程中,随时执行abort,恢复到最开始的内容
git checkout test + git merge develop = git checkout test + git rebase develop
不要对master分支执行rebase,否则会引起很多问题;
一般来说,执行rebase的分支都是自己的本地分支,没有推送到远程版本库
rebase也会出现冲突
解决冲突之后,使用git add添加,然后执行git rebase --continue,接下来Git会继续剩余补丁
任何时候都可以通过git rebase --abort终止rebase,分支会恢复到rebase开始前的状态
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
symlinks = false
ignorecase = true
[remote "origin"]
url = ssh://[email protected]/XXXX/test.git
fetch = +refs/heads/*:refs/remotes/origin/* # +,即使会有冲突依旧会将远程代码拉下来
[branch "master"]
remote = origin
merge = refs/heads/master
[branch "dev-q1"]
remote = origin
merge = refs/heads/dev-q1
[branch "dev-now"]
remote = origin
merge = refs/heads/dev-now
[user]
name = 张三
email = [email protected]
Git遵循三方合并原则
三方合并需要找到共同祖先,不断追溯下去,两条线是并行的,故合并时会遇到冲突;
Git global setup
git config --global user.name "张三"
git config --global user.email "[email protected]"
Create a new repository
git clone ssh://github.com/git-test/t-diff-analysis.git
cd t-diff-analysis
touch README.md
git add README.md
git commit -m "add README"
git push -u origin master
Existing folder
cd existing_folder
git init
git remote add origin ssh://github.com/git-test/t-diff-analysis.git
git add .
git commit -m "Initial commit"
git push -u origin master
Existing Git repository
cd existing_repo
git remote add origin ssh://github.com/git-test/t-diff-analysis.git
git push -u origin --all
git push -u origin --tags