@KalingYu
2017-07-14T08:40:02.000000Z
字数 12685
阅读 781
git
git基础
Git 和其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。这类系统(CVS,Subversion,Perforce,Bazaar 等等)每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容
在保存到 Git 之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的 唯一标识和索引。换句话说,不可能在你修改了文件或目录之后,Git 一无所知。这项特性作为git的设计哲学,建在整体架构的最底层。所以如果文件在传输时变得不完整,或者磁盘损坏导致文件数据缺失,Git 都能立即察觉。
计算校验和的算法是 SHA-1.
当发生一些事情(如提交合并)的时候,Git 调用自己的脚本。有客户端和服务端两种。
将正确命名且可执行的脚本放在 git 目录下的 hooks 子目录中。
1. modified (在工作目录,in working directory)
2. staged (在暂存区域,in staging area)
3. committed (在本地仓库,in git directory(repository))
1. 工作目录: 从项目中取出某个版本的所有文件和目录,用以开始后续工作的叫工作目录。
2. 暂存区域: 放在 git 目录中的一个简单文件,人们常称之为索引文件。
3. 本地仓库:
三个工作区域关系如图
1. 在工作目录中修改某些文件。
2. 对修改后的文件进行快照,然后保存到暂存区域。
3. 提交更新,将保存在暂存区域的文件快照永久转储到 Git 目录中。
把两个分支最新的快照(C3 和 C4)以及二者最新的共同祖先(C2)进行三方合并,合并的结果是产生一个新的提交对象(C5)
最常用的
把在 C3 里产生的变化补丁在 C4 的基础上重新打一遍。
顾名思义,就是挑选另外一个分支历史上某次特定的提交来进行衍合
维护者通常喜欢这么干,以保持一个线性的提交历史
分支其实就是从某个提交对象往回看的历史
这个就不用说了
想像分叉的更新(分支)像是汇流成河的源头(其实提交的历史就是一个链表,当前的提交总是指向上一次的提交:LastTime Commit <- Current Commit),所以上游 upstream 是指最新的提交
git中存在upstream和downstream,简言之,当我们把仓库A中某分支x的代码push到仓库B分支y,此时仓库B的这个分支y就叫做A中x分支的upstream,而x则被称作y的downstream,这是一个相对关系,每一个本地分支都相对地可以有一个远程的upstream分支(注意这个upstream分支可以不同名,但通常我们都会使用同名分支作为upstream)。
sudo apt-get install git git-core
这是来自其它帖子的代码,不过我觉得 git-core 可以不要,因为根据linux社区的说法(如下),这玩意儿已经被废弃了,直接 intall git 就OK了
this(git-core) is a transitional dummy package. the 'git-core' package has been renamed to 'git', which has been installed automatically. this git-core package is now obsolete, and can safely be removed from the system if no other package depends on it.
brew install git
注:brew 是 mac 的包管理 homebrew 的命令。没有 homebrew 的,点这个链接安装homebrew
mkdir demoDir #创建名为demoDir的工作目录
cd demoDir #进入工作目录
git init #初始化 git
echo "# demoDir" >> README.md #创建 README.md 文件
git add README.md #添加文件追踪,偷懒的直接 git add .
git commit -m "first commit" #提交更新,引号里面是更新日志
git remote add origin https://git.coding.net/Kaling/markdown-photos-repository.git #添加远程仓库,origin 是远程仓库名,这个可以自定义。
git push -u origin master #将更新推送到远程仓库
git remote add origin https://git.coding.net/Kaling/markdown-photos-repository.git
#在这里创建或者修改文件,然后用下面的 git commit -am 提交
git commit -am "this is a demo"
git push -u origin master
其实还是git clone [remote-url]
来得简单粗暴
Git有一个工具被称为git config,它允许你获得和设置配置变量;这些变量可以控制Git的外观和操作的各个方面。这些变量可以被存储在三个不同的位置:/etc/gitconfig、~/.gitconfig、.git/config
git config --list
配置文件介绍
1./etc/gitconfig 文件:包含了适用于系统所有用户和所有库的值。如果你传递参数选项’--system’ 给 git config,它将明确的读和写这个文件。
2.~/.gitconfig 文件 :具体到你的用户。你可以通过传递--global 选项使Git 读或写这个特定的文件。
3.位于git目录的config文件 (也就是 .git/config) :无论你当前在用的库是什么,特定指向该单一的库。每个级别重写前一个级别的值。因此,在.git/config中的值覆盖了在/etc/gitconfig中的同一个值。
git config --global user.name "余嘉陵"
git config --global user.email "yujialing94@gmail.com"
cd ~/.ssh
,如果没有秘钥则不会生成这个文件夹,有则备份、删除
ssh-keygen -t rsa -C "yujialing94@gmail.com"
ssh-add <文件名>
git config --global core.autocrlf input
git config --global core.autocrlf true
注:配置换行符是为了避免不同操作系统由换行符不同而导致协作出现状况,详情戳这里。
git config --global core.editor vim
git config --global merge.tool vimdiff
不过我觉得这个没有多大的必要,因为其实 android studio 等Jetbrains那一套IDE有很好都冲突解决工具。
git config --list
git config user.name
git help <verb>
git <verb> --help
man git-<verb>
创建仓库并进行远程工作通常有两种方式:
git init
git remote add
git pull
命令在本地创建仓库,然后添加并拉取远程仓库1)在当前工作目录中初始化仓库
git init
2) 添加远程仓库
git remote add origin https://git.coding.net/Kaling/markdown-photos-repository.git
3) 拉取远程更新
git pull
git clone https://git.coding.net/Kaling/markdown-photos-repository.git
git remote show mp
git tag
新建标签
现在已经有了一个真实的 git 仓库。工作目录下的文件只存在两种状态: 已跟踪和未跟踪。已跟踪的文件是指本来就被纳入版本控制管理的文件,在上次快照中有它们的记录,工作一段时间后,它们的状态可能是未更新,已修改或者已放入暂存区。而所有其他文件都属于未跟踪文件。它们既没有上次更新时的快照,也不在当前的暂存区域。初次克隆某个仓库时,工作目录中的所有文件都属于已跟踪文件,且状态为未修改。
在编辑过某些文件之后,Git 将这些文件标为已修改。我们逐步把这些修改过的文件放到暂存区域,直到最后一次性提交所有这些暂存起来的文件,如此重复。所以使用 Git 时的文件状态变化周期所示。
git status
用 vim 或者你习惯的编辑器创建README 文件,对仓库代码进行说明
vim README
这时用 git status
查看会发现 Untracked files 中出现了 README 这个文件,并提示你用git add <file>
命令添加文件跟踪
git add README
用 git status
查看状态,只要是在 changes to be commited 下面的文件都是已经处于暂存状态的了,而这个时候 README 文件在这个下面。
也是git add <file>
命令。这是个多功能命令,根据目标文件的状态不同,此命令的效果也不同:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。
其中git add *
指暂存、添加所有(.gitignore中限制的除外)文件到已跟踪状态。
创建 .gitignore 文件
vim .gitignore
文件 .gitignore 的格式规范如下:
ps:glob 模式说明。所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。星号(*)匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。
代码示例:
# 此为注释 – 将被 Git 忽略
# 忽略所有 .a 结尾的文件
*.a
# 但 lib.a 除外
!lib.a
# 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO
/TODO
# 忽略 build/ 目录下的所有文件
build/
# 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
doc/*.txt
git diff
git diff --staged
git diff --cached
git add . #将已跟踪的文件全部添加到暂存区
git commit -m #添加更新的说明,可以不添加参数m 而使用文件编辑器添加更多的说明
或者直接使用git commit -am
, -a 会直接将已经追踪的文件提交。
git rm <file>
git rm --cached <file>
git mv README.txt README
这句话相当于
mv README.txt README
git rm README.txt
git add README
git log
常用 -p 选项展开显示每次提交的内容差异,用 -2 则仅显示最近的两次更新。
git commit --amend
如果刚才提交时忘了暂存某些修改,可以先补上暂存操作,然后再运行 --amend 提交, 以下三条命令最终只是产生一个提交,第二个提交命令修正了第一个的提交内容。
git commit -m 'initial commit'
git add forgotten_file
git commit --amend
git remote
git remote 命令常用参数说明:
使用git remote add [remote-name] [url]
命令
git remote add mp https://git.coding.net/Kaling/markdown-photos-repository.git
这个时候, mp 就指代了这个远程仓库。
git fetch [remote-name]
命令。这样就可以访问远程仓库的所有分支数据,并可以在这之后自己手工将某个分支合并到本地某个分支。
git fetch [remote-name]
git pull
git pull <remote-branch> : <local-branch> #只 pull 一个远程分支
如果当前分支与多个主机存在追踪关系,则可以使用-u选项指定一个默认主机,这样后面就可以不加任何参数使用git push。
git push -u [remote-name] [local-branch-name]
如果当前分支只有一个追踪分支,那么主机名都可以省略。
git push
将本地的 branch-name 分支推送到 remote-name 远程服务器上。
git push [remote-name] [local-branch-name]:[remote-branch-name]
git push
默认是提交当前本地分支追踪的同名的
如果省略远程分支名,则表示将本地分支推送与之存在”追踪关系”的远程分支(通常两者同名),如果该远程分支不存在,则会被新建。
git push [remote-name] [local-branch-name]
如果当前分支与远程分支之间存在追踪关系,则本地分支和远程分支都可以省略,直接将当前分支推送到origin主机的对应分支。
git push [remote-name]
不管是否存在对应的远程分支,将本地的所有分支都推送到远程主机,这时需要使用–all选项。
git push --all [remote-name]
如果远程主机的版本比本地版本更新,推送时Git会报错,要求先在本地做git pull合并差异,然后再推送到远程主机。这时,如果你一定要推送,可以使用–force选项。
git push --force origin
结合git pull
的参数,会发现后面的分支顺序是<来源地>:<目的地>.
删除远程分支。
git push [remote-name] :master
#等同于
git push [remote-name] --delte master
git push不会推送标签(tag),除非使用–tags选项。
git push [remote-name] --tags
标签分为两种:轻量级的(lightweight) 和含附注(annotated)的 。前者是指向一个特定提交的引用,后者是存贮在仓库中的一个独立对象,有自身的校验信息和标签说明等。一般建议使用含附注的标签。
- 新建含附注的标签。-a
是annotated
(附注) 的首字母。m
则是message
git tag -a v1.4 -m "my version 1.4
git tag v1.4-lightweight
git show v1.4
你创建了很多个分支,Git 如何知道你现在在哪个分支上工作呢?
事实上它保存着一个名为HEAD 的特别指针,指向当前分支。可以用git checkout branch-name
切换 HEAD 指向的分支,以达到切换分支的目的。
git branch
git branch -vv #查看分支的追踪
git branch -a
git branch new-branch-name
git checkout new-branch-name
git checkout --track origin/dev #切换到远程的 dev 分支
git branch -d branch-name
合并分支分为 git merge 和 git rebase,两者都是将一个分支的代码合入另外一个分支,但是原理b不一样。
优点:merge是一个安全的操作,现有的分支不会被更改,避免了rebase潜在的缺点。
缺点:会带来额外的合并提交,如果merge比较频繁的话,或多或少会污染分支历史,增加项目历史的难度。
git checkout mywork
git merge origin
rebase为原分支的每一个提交都创建了一个新的提交,重写了项目历史,不会带来合并提交。
优点:最大的好处是你的项目历史会非常整洁,是一条漂亮的线性历史,不会像git merge那样引入不必要的合并提交。
缺点:使用rebase会带来两种后果:安全性和可追踪性。如果违反了rebase黄金法则,重写项目历史可能会给协作带来灾难性的影响。此外rebase不会有合并提交中附带的信息。
git chekcout mywork
git rebase origin
git branch -m [old-branh] [new-branch]
git push --delete orgin [remote-branch-name]
git push origin test:master # 提交本地的 test 的分支到 远程的 master 分支
git branch --set-upstream local-branch-name origin/remote-branch-name
git checkout orgin/remote-branch-name -b local-branch-name
所谓的重命名远程分支也就是先删除远程分支,然后重命名本地分支,最后将本地分支推送到远程分支
git push --delete origin [remote-branch-name] #删除远程分支
git branch -m [old-branch-name] [new-branch-name] #重命名本地分支
git push origin [new-branch-name] #推送本地分支到远程
打补丁有两种方案 git diff 和 git forma-patch, 前者生成通用的补丁,可用于各种版本控制工具;后者生成git 专用补丁,可用git am 「补丁文件名」
这条命令打上。推荐使用后者。使用过程如下
# 在需要生成补丁的分支中打补丁,这会将所有的提交都打成一个补丁。M参数表示和后面的值表示和 master 分支进行对比
git format-patch -M master
# 这会生成补丁文件,比如补丁文件名为 0001-.patch. 切换到 master 分支,新建并切换到 patch 分支,然后打补丁
git am 0001-.patch
工作流程举栗
总结
艾玛,这玩意儿编辑起来太累了,如果要理解git的工作方式就直接看上面的那个,关于私有团队的协作还是直接点这个链接吧。
同上理,点这个链接
同上上理,点这个链接
$ git remote add jessica git://github.com/jessica/myproject.git
$ git fetch jessica
$ git checkout -b rubyclient jessica/ruby-client
例如在issue01特性分支上屏蔽master分支的提交历史
git log issue01 --not master
创建基于jessica的ruby-client的新分支ruby-client
git checkout -b rubyclient jessica/ruby-client
用SHA-1中开头4个字符(及以上)就能标识/引用/索引那一次 commit
经常有这样的事情发生,当你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态,而你想转到其他分支上进行一些工作。问题是,你不想提交进行了一半的工作,否则以后你无法回到这个工作点。
解决这个问题就是用 git stash
命令,获取工作目录中的中间状态,将变更保存起来,并随时切回来重用。
步骤:
1. 用 git status
查看中间状态
2. 使用 git stash
命令暂存当前变更
3. 可以用 git status
验证当前并没有待提交的变更
4. 查看当前的储藏 git stash list
5. 切回分支应用储藏 git stash apply
(可添加一个 stash 名字作为参数,如无则默认使用最近的储藏)
// todo
// todo
相关文档:http://dudu.zhihu.com/story/8981865
1. 分批递交,降低commit的颗粒度
比如,你修改了 a.py, b.py, c.py, d.py,其中 a.py 和 c.py 是一个功能相关修改,b.py,d.py属于另外一个功能相关修改。那么你就可以采用:
git stage a.py c.py
git commit -m "function 1"
git stage b.py d.py
git commit -m "function 2"
分阶段提交
比如,你修改了文件 hello.py,修改了一些以后,做了 git stage heello.py动作,相当于对当前的hello.py 做了一个快照, 然后又做了一些修改,这时候,如果直接采用 git commit 递交,则只会对第一次的快照进行递交,当前内容还保存在 working 工作区。
最佳实践
做了阶段性修改,但是还不能做一次递交,这时先 git stage 一下
如果有问题,可以随时 checkout 回退
递交之前,使用 git status,git diff HEAD 仔细查看是否需要的递交
git commit -a ,保证递交了所有内容
从根本上来讲 Git 是一套内容寻址 (content-addressable) 文件系统,在此之上提供了一个 VCS 用户界面。
持续更新中……
Pro Git 中文版
git-core. Fast, scalable, distributed revision control system (obsolete)
Git 分支 - 分支的衍合
git push 与 pull 的默认行为