教程网站:https://git-scm.com/book/zh/v2/
git 工具(MINGW64)下载网址: https://npm.taobao.org/mirrors/git-for-windows/
官网: https://gitforwindows.org/
*windows下的powershell命令行工具据说很强大
*https://help.github.com/articles/connecting-to-github-with-ssh/ [如何关联github和自己的本地主机]
执行ssh-add时出现Could not open a connection to your authentication agent,则只要运行如下命令即可:ssh-agent bash
git status无法显示中文文件名(八进制)
git 默认中文文件名是 \xxx\xxx 等八进制形式,是因为 对0x80以上的字符进行quote ,只需要
git config –global core.quotepath false
core.quotepath设为false的话,就不会对0x80以上的字符进行quote。中文显示正常
关于本地修改之后,git push前 本地提交和从远程拉取 的关系:
※git pull [origin] [master]拉取时只拉取本地与远程不同的部分(git push推送时也是如此),而git如何比较不同呢?就是通过比较 本地的commit日志和远程的commit日志。所以git push前正确的顺序是先在本地commit提交,然后git pull(此时git做的工作如下:git比较本地commit和远程commit,将共同节点之后的远程提交拉取下来和共同节点之后的本地提交合并----此时如果两个远程和本地都修改了同一个文件的同一行则会产生冲突,需要手动解决冲突----,合并之后的提交是最新的--即在当前远程提交的最新提交之前--然后就可以git push origin master了,此时将最新产生的提交push到远程分支上)。
※另外,如果本地修改之后先执行git pull 操作,则此时依然是比较本地的提交和远程提交的不同,然后拉取不同的文件,此时若本地最新的提交之后的修改文件没有涉及到远程的需要拉取下来的文件,则此时git允许git pull拉取下来,否则则会提示共同修改的文件会被覆盖从而放弃执行git pull(而不是尝试合并文件,只有对已经提交的变化git才会执行合并操作,未提交的不会),提示先执行git commit 或git stash然后再执行git pull。
※关于git pull之后git在本地是如何合并的?
假设远程分支origin/master和本地分支master的最新的共同节点为
※git push之前必须执行git pull操作,即使别人推送到远程仓库中的文件和你毫无关系也必须先pull一下。也就是说,合并的操作一定是在你的本地进行的。
关于每次远程拉取和提交都需要输入用户名和密码:
原因是在添加远程库的时候使用了https的方式。。所以每次都要用https的方式push到远程库
查看使用的传输协议:
git remote -v
wuxiao@wuxiao-C-B150M-K-Pro:~/MyGithub/DailyBlog$ git remote -v
origin https://github.com/toyijiu/DailyBlog.git (fetch)
origin https://github.com/toyijiu/DailyBlog.git (push)
重新设置成ssh的方式:
git remote rm origin
git remote add origin git@github.com:username/repository.git
git push -u origin master
再看下当前的传输协议:
wuxiao@wuxiao-C-B150M-K-Pro:~/MyGithub/DailyBlog$ git remote -v
origin [email protected]:toyijiu/DailyBlog.git (fetch)
origin [email protected]:toyijiu/DailyBlog.git (push)
如果你没有拉取的仓库的ssh网址的密码,也可以使用下面的解决方法:
git config --global credential.helper store
然后你会在你本地生成一个文本,上边记录你的账号和密码。然后你使用上述的命令配置好之后,再操作一次git pull,然后它会提示你输入账号密码,这一次之后就不需要再次输入密码了。
Git:解决head detached at xxxx:
起因
希望将同步远程分支,执行了如下命令
git checkout origin/branch
导致HEAD指向了一个非本地的分支,进入detached-HEAD状态,这种状态非常危险,如果在此状态下commit和修改代码很容易丢失,因为并没有本地分支保存你的修改
附:同步远程分支的正确命令,以下两个命令都可以
#这条命令可以自定义本地branch名
git checkout -b localBranchName origin/branchName
git checkout --track origin/branchName
解决
情况一:
如果你在执行完错误的命令后立刻发现了问题,并且没有commit,可以通过以下命令将HEAD指向新建的本地分支localBranch,这时就解决了detached状态,并且此时的localBranch已经和origin/branch关联了,可以放心修改/提交代码
git checkout -b localBranch
情况二
如果已经checkout后,commit了大量代码,就要小心了。
我们解决的思路是:
- 从当前的detached HEAD新建一个缓存分支,保存当前已经commit的修改
- 新拉取一个希望同步的远程分支到本地
- 将缓存分支合并到希望同步的分支上
通过reflog确定最后一次提交的操作id,如1ebf663
git reflog
从这个操作新建一个缓存分支temp
git branch temp 1ebf663
拉取希望同步的远程分支,执行该命令后会自动切换到targetBranch上
git checkout --track origin/targetBranch
将缓存分支合并到希望同步的分支上
git merge temp
接下来就可以放心提交了~
--END
蒋鑫 Git权威指南学习笔记,这是一本手册性的书籍
☯ git config
☯git config
- git config alias.st status //版本库级别的配置,只对当前版本库起作用, 对应的配置文件在: path/to/my/workspace/demo/.git/config
- git config --global alias.st status //用户级别的配置,当前用户的所有版本库都可以使用.对应的配置文件位置:/home/ths/.gitconfig
- git config --system alias.st status //系统级别的配置,所有用户的所有版本库都可以使用。对应的配置文件位置:/root/.gitconfig. (有的在/etc/.gitconfig)
git config --global core.eidtor vim //将git默认的编辑器配置为vim
git config --global push.default simple//将push.default设置为simple模式。关于push.default:
当使用git push推送而不加任何参数时,push.default参数就起作用了。可以通过man git config 命令查看官方的文档中关于push.default的说明。push.default一些值的说明:
· nothing - do not push anything (error out) unless a refspec is explicitly given. This is primarily meant for people who want to avoid mistakes by always being explicit.
· current - push the current branch to update a branch with the same name on the receiving end. Works in both central and non-central workflows.【将当前分支推送到接收端同名的分支】
· upstream - push the current branch back to the branch whose changes are usually integrated into the current branch (which is called @{upstream}). This mode only makes sense if you are pushing to the same repository you would normally pull from (i.e. central workflow). [将当前分支推送到上游分支。这个参数只适用于 拉取和推送的远程服务器是同一个的情况 (即所谓的中心化工作流)。]
· simple - in centralized workflow, work like upstream with an added safety to refuse to push if the upstream branch’s name is different from the local one. When pushing to a remote that is different from the remote you normally pull from, work as current. This is the safest option and is suited for beginners. 【在中心化工作流中, 此模式和current模式类似,但是加了一个安全限制:如果本地分支名和上 游分支名不一样,git将拒绝推送。如果不是中心化工作流(即拉取的远程服务器和将要推送的远程服务器不是同一个),这个模式和current模式一样。】
This mode has become the default in Git 2.0. [这个模式在git2.0版本之后是起默认模式]
· matching - push all branches having the same name on both ends. This makes the repository you are pushing to remember the set of branches that will be pushed out (e.g. if you always push maint and master there and no other branches, the repository you push to will have these two branches, and your local maint and master will be pushed there).【这个模式会将本地与远程服务器上 所有同名的 分支一并推送过去】
暂存区称为stage或index,也叫索引区git diff HEAD表示当前工作区与HEAD(版本库中的最新提交)的差异
git diff
git diff HEAD
git diff --cached/--staged
git cat-file -t sha1// type
git cat-file -p sha1//content
.git/refs目录下的结构:
目录.git/refs是保存引用的命名空间,其中.git/refs/heads目录下的引用又称为分支。对于分支,既可以使用正规的长格式表示法refs/heads/master,也可以去掉前面的两极目录用master来表示。
git rev-parse [master] //用于显示指定的引用对应的提交ID。
.git/HEAD---->.git/refs/heads/maser--->commitID---->①目录树②父提交
git reset 改变的是.git/refs/master中的指向。但是带路径的用法不改变其内容(即master的指向),只是用来将指定的commit中的文件替换掉 暂存区 的文件。不加commit默认为HEAD
git checkout 改变的是.git/HEAD中的指向。但是带路径的用法不改变其内容,只是用来将指定的commit中的文件替换掉 暂存区和工作区中的文件,不加commit则是将暂存区中的文件替换掉工作区中的文件。[注意:不是完全覆盖,只是覆盖commit中已有的文件!!!这句话很关键]
使用reflog挽救错误的重置,.git/logs目录下的日志文件记录了分支或HEAD的变更。
git reflog show [master] -n //显示.git/logs/refs/heads/master下的日志文件(格式经过整理了)。如果不加maser默认为HEAD,即显示.git/logs/HEAD中的日志文件。show关键字貌似可用可不用..
git stash
git stash实际上用到了git reset --hard HEAD命令。
.git/refs/stash //git stash会将进度保存在引用refs/stash文件所指向的提交中。多次的进度保存实际上相当于引用refs/stash文件一次又一次的变化,而refs/stash引用的变化由reflog(即.git/logs/refs/stash文件)所记录下来。
git reflog show refs/stash //显示各次stash的变化日志
git log refs/stash //提交历史中包含了stash所做的提交。
改变历史:挑选操作,变基操作,交互式变基操作
①挑选操作:git cherry-pick
其含义是从众多的提交中挑选出一个提交应用在当前的工作分支中。该命令需要提供一个提交ID作为参数,操作过程相当于将该提交导出为补丁文件,然后在当前HEAD上重放,形成无论内容还是提交说明都一致的提交。
②变基操作
对提交执行变基操作,即可以实现将指定范围的提交“嫁接”到另外一个提交之上。
③交互式变基操作
[当你觉得某项技术很难理解时,换本书看!]
https://git-scm.com/book/zh/v2# 学习笔记
*格式:第一部分:1,2,3,4, 第二部分,1, 2,3,4
第一部分:Git基础知识
0,
git 分为3个区域:①工作区;②暂存区; ③仓库区。其中暂存区和仓库区都是在.git目录中。
Git 保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照(即当时的所有内容)。
1:clone
$ git clone https://github.com/libgit2/libgit2
这会在当前目录下创建一个名为 “libgit2” 的目录,并在这个目录下初始化一个
.git
文件夹,从远程仓库拉取下所有数据放入.git
文件夹,然后从中读取最新版本的文件的拷贝。 如果你进入到这个新建的libgit2
文件夹,你会发现所有的项目文件已经在里面了,准备就绪等待后续的开发和使用。 如果你想在克隆远程仓库的时候,自定义本地仓库的名字,你可以使用如下命令:
$ git clone https://github.com/libgit2/libgit2 mylibgit
2:添加到暂存区
git add
命令。 这是个多功能命令:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时把有冲突的文件标记为已解决状态等。 将这个命令理解为“添加内容到下一次提交中”而不是“将一个文件添加到项目中”要更加合适。
git add -A
stages Allgit add .
stages new and modified, without deletedgit add -u
stages modified and deleted, without new
3:忽略文件列表
文件
.gitignore
的格式规范如下:
- 所有空行或者以
#
开头的行都会被 Git 忽略。
可以使用标准的 glob 模式匹配。
匹配模式可以以(
/
)开头防止递归。匹配模式可以以(
/
)结尾指定目录。要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(
!
)取反。所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。 星号(
*
)匹配零个或多个任意字符;[abc]
匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?
)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如[0-9]
表示匹配所有 0 到 9 的数字)。 使用两个星号(*
) 表示匹配任意中间目录,比如`a/**/z` 可以匹配a/z
,a/b/z
或 `a/b/c/z`等。我们再看一个 .gitignore 文件的例子:
# no .a files *.a # but do track lib.a, even though you're ignoring .a files above !lib.a # only ignore the TODO file in the current directory, not subdir/TODO /TODO # ignore all files in the build/ directory build/ # ignore doc/notes.txt, but not doc/server/arch.txt doc/*.txt # ignore all .pdf files in the doc/ directory doc/**/*.pdf
4:差异
git diff [filelist]命令不加参数比较工作区和暂存区之间的差异(以暂存区为基准,因为工作区是更加新的内容)
git diff --staged [filelist]比较暂存区和仓库区之间的差异(以仓库区为基准,因为暂存区是更加新的内容).--cached和--staged是同义词。
5:提交
※每一次运行提交操作,都是对你项目作一次快照(快照都是在仓库里都),以后可以回到这个状态,或者进行比较。
※git commit -a : -a选项可以自动把已经跟踪(跟踪的意思即是此文件已经被暂存过)过的文件暂存起来并提交,跳过了暂存命令。但是注意,这个文件必须已经被跟踪过,否则无法直接提交到仓库。
6:移除文件
※ git rm [file] 将file从暂存区以及工作区删除。如此此文件以后便不会再被git管理。使用git rm时注意,如果file有数据尚未被提交到git仓库中,那么git rm会出错。必须使用-f参数强制删除。这是一种保护机制,用于防止误删还没有添加到快照(仓库)中的数据,而这样的数据git是无法恢复的。
※另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。 换句话说,你想让文件保留在磁盘,但是并不想让 Git 继续跟踪。 当你忘记添加
.gitignore
文件,不小心把一个很大的日志文件或一堆.a
这样的编译生成文件添加到暂存区时,这一做法尤其有用。 为达到这一目的,使用--cached
选项:
$ git rm --cached README
git rm
命令后面可以列出文件或者目录的名字,也可以使用glob
模式。 比方说:
$ git rm log/\*.log
注意到星号
*
之前的反斜杠\
, 因为 Git 有它自己的文件模式扩展匹配方式,所以我们不用 shell 来帮忙展开(即让*保持自己的含义,git会识别*然后匹配)。 此命令删除log/
目录下扩展名为.log
的所有文件。 类似的比如:
$ git rm \*~
该命令为删除以
~
结尾的所有文件
7, 移动文件
$ git mv file_from file_to
它会恰如预期般正常工作。 实际上,即便此时查看状态信息,也会明白无误地看到关于重命名操作的说明:
$ git mv README.md README
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD..." to unstage) renamed: README.md -> README
其实,运行 git mv 就相当于运行了下面三条命令:
$ mv README.md README
$ git rm README.md
$ git add README
如此分开操作,Git 也会意识到这是一次改名,所以不管何种方式结果都一样。 两者唯一的区别是,mv 是一条命令而另一种方式需要三条命令,直接用 git mv 轻便得多。 不过有时候用其他工具批处理改名的话,要记得在提交前删除老的文件名,再添加新的文件名。
8:历史 git log
※一个常用的选项是
-p
,用来显示每次提交的内容差异。 你也可以加上-2
来仅显示最近两次提交.※每次提交的简略的统计信息,你可以使用
--stat
选项.※另外一个常用的选项是
--pretty
。 这个选项可以指定使用不同于默认格式的方式展示提交历史。 这个选项有一些内建的子选项供你使用。 比如用oneline
将每个提交放在一行显示,查看的提交数很大时非常有用。 另外还有short
,full
和fuller
可以用,展示的信息或多或少有些不同。其中最有意思的子选项是format,可以定制要显示的记录格式。如:
git log --pretty=format:"%h - %an, %ar : %s"
常用的选项如下:
Table 1. git log --pretty=format
常用的选项选项 说明
%H
提交对象(commit)的完整哈希字串
%h
提交对象的简短哈希字串
%T
树对象(tree)的完整哈希字串
%t
树对象的简短哈希字串
%P
父对象(parent)的完整哈希字串
%p
父对象的简短哈希字串
%an
作者(author)的名字
%ae
作者的电子邮件地址
%ad
作者修订日期(可以用 --date= 选项定制格式)
%ar
作者修订日期,按多久以前的方式显示
%cn
提交者(committer)的名字
%ce
提交者的电子邮件地址
%cd
提交日期
%cr
提交日期,按多久以前的方式显示
%s
提交说明
作者 和 提交者 之间究竟有何差别, 其实作者指的是实际作出修改的人,提交者指的是最后将此工作成果提交到仓库的人。 所以,当你为某个项目发布补丁,然后某个核心成员将你的补丁并入项目时,你就是作者,而那个核心成员就是提交者。 我们会在 分布式 Git 再详细介绍两者之间的细微差别
※当 oneline 或 format 与另一个
log
选项--graph
结合使用时尤其有用。 这个选项添加了一些ASCII字符串来形象地展示你的分支、合并历史。以下表是git log 的所有可用参数及其说明
Table 2. git log
的常用选项选项 说明
-p
按补丁格式显示每个更新之间的差异。
--stat
显示每次更新的文件修改统计信息。
--shortstat
只显示 --stat 中最后的行数修改添加移除统计。
--name-only
仅在提交信息后显示已修改的文件清单。
--name-status
显示新增、修改、删除的文件清单。
--abbrev-commit
仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。
--relative-date
使用较短的相对时间显示(比如,“2 weeks ago”)。
--graph
显示 ASCII 图形表示的分支合并历史。
--pretty
使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。
※限制输出长度
除了定制输出格式的选项之外,git log 还有许多非常实用的限制输出长度的选项,也就是只输出部分提交信息。 之前你已经看到过 -2 了,它只显示最近的两条提交, 实际上,这是 -
选项的写法,其中的 n 可以是任何整数,表示仅显示最近的若干条提交。 不过实践中我们是不太用这个选项的,Git 在输出所有提交时会自动调用分页程序,所以你一次只会看到一页的内容。 另外还有按照时间作限制的选项,比如 --since 和 --until 也很有用。 例如,下面的命令列出所有最近两周内的提交:
$ git log --since=2.weeks
这个命令可以在多种格式下工作,比如说具体的某一天 "2008-01-15",或者是相对地多久以前 "2 years 1 day 3 minutes ago"。还可以给出若干搜索条件,列出符合的提交。 用 --author 选项显示指定作者的提交,用 --grep 选项搜索提交说明中的关键字。 (请注意,如果要得到同时满足这两个选项搜索条件的提交,就必须用 --all-match 选项。否则,满足任意一个条件的提交都会被匹配出来)
另一个非常有用的筛选选项是 -S,可以列出那些添加或移除了某些字符串的提交。 比如说,你想找出添加或移除了某一个特定函数的引用的提交,你可以这样使用:
$ git log -S function_name
最后一个很实用的 git log 选项是路径(path), 如果只关心某些文件或者目录的历史提交,可以在 git log 选项的最后指定它们的路径。 因为是放在最后位置上的选项,所以用两个短划线和空格(“-- ”)隔开之前的选项和后面限定的路径名。如git log -- tmp.txt
Table 3. 限制 git log
输出的选项选项 说明
-(n)
仅显示最近的 n 条提交
--since
,--after
仅显示指定时间之后的提交。
--until
,--before
仅显示指定时间之前的提交。
--author
仅显示指定作者相关的提交。
--committer
仅显示指定提交者相关的提交。
--grep
仅显示含指定关键字的提交
-S
仅显示添加或移除了某个关键字的提交
9, 撤销操作
9.1 注意,有些撤消操作是不可逆的。 这是在使用 Git 的过程中,会因为操作失误而导致之前的工作丢失的少有的几个地方之一。
9.2, 有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 --amend 选项的提交命令尝试重新提交:
$ git commit --amend
git commit --amend命令可以将暂存区中的文件提交到仓库替换掉仓库中最近一次的版本。①如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令),那么快照会保持不变,而你所修改的只是提交信息。文本编辑器启动后,可以看到之前的提交信息。 编辑后保存会覆盖原来的提交信息。
②你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend
最终你只会有一个提交 - 第二次提交将代替第一次提交的结果。9.3, 取消暂存区中的文件
git reset HEAD [file] //此命令将file文件从暂存区中删除,变为未暂存的状态
note: 虽然在调用时加上
--hard
选项可以令git reset
成为一个危险的命令(译注:可能导致工作目录中所有当前进度丢失!),但本例中工作目录内的文件并不会被修改。 不加选项地调用git reset
并不危险 — 它只会修改暂存区域。9.4, 撤销对工作区文件的修改
git checkout -- [file] //此命令将file文件撤销到上一次提交到仓库中的状态。
note: 这是一个危险的命令。 你对那个文件做的任何修改都会消失 - 你只是拷贝了另一个文件来覆盖它。 除非你确实清楚不想要那个文件了,否则不要使用这个命令。
如果你仍然想保留对那个文件做出的修改,但是现在仍然需要撤消,我们将会在 Git 分支 介绍保存进度与分支;这些通常是更好的做法。
9.5, 记住,在 Git 中任何 已提交的 东西几乎总是可以恢复的。 甚至那些被删除的分支中的提交或使用
--amend
选项覆盖的提交也可以恢复(阅读 数据恢复 了解数据恢复)。 然而,任何你未提交的东西丢失后很可能再也找不到了。
10, 远程仓库的使用
// 场景: 将本地仓库关联GitHub自建的某个仓库并推送本地代码
1,git remote add origin githubUrl // 添加远程仓库地址.
2,git fetch // 拉取远程分支名称。fetch 之前使用git branch命令可以看到没有远程分支origin/master
3,git branch -u origin/master master // 将本地master分支设置为跟踪远程分支master,其中origin/master是本地的远程分支。
4,git pull --allow-unrelated-histories
5,git push [如果 push.default 未配置,会有个警告信息。可以使用如下命令消除:git config --global push.default simple / matching。其中simple意思是推送当前分支到git pull拉取的那个远程分支。matching意思是推送当前分支到远程存在的同名分支上去。simple更保险一点]
如果没有第三步,也可以在执行2之后执行如下两个命令,效果一样:
3,git pull origin master --allow-unrelated-histories
4. git push -u origin master (第一次推送时加上-u参数,将会设置本地分支跟踪远程分支。以后再拉或推就可以直接git pull / git push 而不必指定 remote branch了)
git remote //查看每一个远程服务器(或者说远程仓库)的简写。如果说是克隆的仓库,至少有一个origin,这是git给克隆仓库服务器的默认名字。
git remote -v //查看简写及其对应的URL
git remote add
//添加一个新的远程Git仓库,同时为其指定一个简写别名。 git remote rm [remote-repository name] // 移除一个远程仓库。
git remote show [remote-name] //check more info of a remote repository
git remote rename [oldname] [newname] // rename a remote repository, 这个命令同样会修改你的远程分支名称。如:oldname/master ---> newname/master
git remote set-url origin 'http://gs.mizss.com/hanfeng/eiduo.git' //重新设置远程仓库origin的地址,比如当远程项目改名字了就需要重新设置一下。
git ls-remote [remote] //显式获取远程引用的完整列表, remote指远程服务器(仓库)名
git remote show [remote] // 获取远程分支的更多信息
origin/master 是本地的一个分支,它指向服务器仓库origin的master分支
git clone -o [clone_name] //克隆的远程分支名默认为clone_name / master
git push [remote] [branch] 【此命令实际上是git push [remote] [local_branch]:[remote_branch]的简写,表示本地分支和远程分支名称相同】
git push [remote] [local_branch]:[remote_branch] 【本地和远程分支名称可以不同.如果远程还没有相应分支,则会自动创建名称为remote_branch的分支,但是之后每次都需要指定remote 和 branch,不太方便,所以第一次最好使用-u标记的push,使用-u标记之后,每次都可以使用git push代替此命令,而不必指定remote和branch了。见下个命令。】
git push -u [remote] [local_branch]:[remote_branch]
-u
标记;这是--set-upstream
的简写,设置上游分支即可将remote_branch与本地的local_branch关联起来,只需要在第一次推送时使用。使用该标记之后就可以使用git push 代替 git push [remote] [branch]了。【使用场景:只有本地有分支,远程没有对应分支时,注意冒号前后不能有空格】(上游分支是指以本地的origin/master代表的远程的master分支, origin/master是本地的远程分支, 可能与远程仓库不一样,通过git pull origin master 与远程仓库保持同步。)git fetch origin //
git pull // 相当于git fetch 和git merge两个命令。当远程仓库和本地仓库是两个独立的仓库时,第一次 git pull 会报错:fatal: refusing to merge unrelated histories,使用下面命令即可。git pull origin master --allow-unrelated-histories
git checkout -b [new_branch] //基于当前分支创建一个新的分支并立即切换到新分支上
git checkout -b [new_branch] [existing_branch] // 基于existing_branch分支创建一个新的分支并立即切换到新分支上。
git checkout -b [new_branch] [commit_id] //基于当前分支的某次提交创建一个新的分支并立即切换到新的分支上。
git checkout -b [local_branch] [remote/branch]//基于远程分支在本地创建一个新的分支并立即切换到新的分支上去。//这个是拉取本地不存在的远程分支的方法!!如果本地还没有远程分支,使用 git fetch 将新的远程分支拉取到本地,然后再执行上述命令。
git branch -u [remote / branch] [local_branch]//将当前分支设置为跟踪[remote/branch]的分支
git branch --set-upstream-to=origin/master master // 作用同上
git branch -vv (注意是两个v不是w) //将所有的本地分支列出来并且包含更多的信息。
git push orgin --delete [branch] //删除远程分支。这个命令做的只是从服务器上移除这个指针。 Git 服务器通常会保留数据一段时间直到垃圾回收运行,所以如果不小心删除掉了,通常是很容易恢复的
git merge的完全使用手册:https://www.jianshu.com/p/58a166f24c81
git fetch [remote-name] //从远程仓库中拉取数据
这个命令会访问远程仓库,从中拉取所有你还没有的数据。 执行完成后,你将会拥有那个远程仓库中所有分支的引用,可以随时合并或查看。
如果你使用 clone 命令克隆了一个仓库,命令会自动将其添加为远程仓库并默认以 “origin” 为简写。 所以,git fetch origin 会抓取克隆(或上一次抓取)后新推送的所有工作。 必须注意 git fetch 命令会将数据拉取到你的本地仓库 - 它并不会自动合并或修改你当前的工作。 当准备好时你必须手动将其合并入你的工作。
如果你有一个分支设置为跟踪一个远程分支(阅读下一节与 Git 分支 了解更多信息),可以使用 git pull 命令来自动的抓取然后合并远程分支到当前分支。 这对你来说可能是一个更简单或更舒服的工作流程;默认情况下,git clone 命令会自动设置本地 master 分支跟踪克隆的远程仓库的 master 分支(或不管是什么名字的默认分支)。 运行 git pull 通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。
git push [remote-name] [branch-name]//推送到远程仓库
当你想分享你的项目时,必须将其推送到上游。 这个命令很简单:git push [remote-name] [branch-name]。 当你想要将 master 分支推送到 origin 服务器时(再次说明,克隆时通常会自动帮你设置好那两个名字),那么运行这个命令就可以将你所做的备份到服务器:$ git push origin master
只有当你有所克隆服务器的写入权限,并且之前没有人推送过时,这条命令才能生效。 当你和其他人在同一时间克隆,他们先推送到上游然后你再推送到上游,你的推送就会毫无疑问地被拒绝。 你必须先将他们的工作拉取下来并将其合并进你的工作后才能推送。 阅读 Git 分支 了解如何推送到远程仓库服务器的详细信息。
11, 打标签
1, Git可以给历史中的某一个提交打上标签,以示重要,比如标记发布节点(v1.0等等)。
2, 列出标签
git tag
git tag -l 'v1.0*' //只列出v1.0相关的标签。
3,创建 标签
3.0, 从远程仓库拉取代码时,git pull并不会将标签拉下来,git fetch 则会将标签拉下来。
Git 使用两种主要类型的标签:轻量标签(lightweight)与附注标签(annotated)。
一个轻量标签很像一个不会改变的分支 - 它只是一个特定提交的引用。
然而,附注标签是存储在 Git 数据库中的一个完整对象。 它们是可以被校验的;其中包含打标签者的名字、电子邮件地址、日期时间;还有一个标签信息;并且可以使用 GNU Privacy Guard (GPG)签名与验证(-s参数)。 通常建议创建附注标签,这样你可以拥有以上所有信息;但是如果你只是想用一个临时的标签,或者因为某些原因不想要保存那些信息,轻量标签也是可用的。
3,1, 附注标签
在 Git 中创建一个附注标签是很简单的。 最简单的方式是当你在运行
tag
命令时指定-a
选项:
$ git tag -a v1.4 -m 'my version 1.4'
通过使用
git show
命令可以看到标签信息与对应的提交信息。输出显示了打标签者的信息、打标签的日期时间、附注信息,然后显示具体的提交信息。 3.2, 轻量标签
另一种给提交打标签的方式是使用轻量标签。 轻量标签本质上是将提交校验和存储到一个文件中 - 没有保存任何其他信息。 创建轻量标签,不需要使用
-a
、-s
或-m
选项,只需要提供标签名字:$ git tag v1.4
这时,如果在标签上运行
git show
,你不会看到额外的标签信息。 命令只会显示出提交信息。3.3, 为过去的某次历史提交打标签
要在那个提交上打标签,你需要在命令的末尾指定提交的校验和(或部分校验和):
$ git tag -a v1.2 8fcdb32
3.4, 共享标签
※ 默认情况下,git push 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。 这个过程就像共享远程分支一样 - 你可以运行 git push origin [tagname] / git push origin
[tagname]。 ※ 如果想要一次性推送很多标签,也可以使用带有
--tags
选项的git push
命令 git push origin --tags。 这将会把所有不在远程仓库服务器上的标签全部传送到那里。现在,当其他人从仓库中克隆或拉取,他们也能得到你的那些标签。
3.5,删除标签
※删除本地标签: git tag -d v1.0.0 (或 git tag --delete v1.0.0)
※删除远程仓库标签:git push origin
-d (--delete) v1.0.0
4,检出标签
在 Git 中你并不能真的检出一个标签,因为它们并不能像分支一样来回移动。 如果你想要工作目录与仓库中特定的标签版本完全一样,可以使用 git checkout -b [branchname] [tagname] 在特定的标签上创建一个新分支:
$ git checkout -b version2 v2.0.0
Switched to a new branch 'version2'
当然,如果在这之后又进行了一次提交,version2 分支会因为改动向前移动了,那么 version2 分支就会和 v2.0.0 标签稍微有些不同,这时就应该当心了。
12, Git别名
git config --global alias.co checkout
可以看出,Git 只是简单地将别名替换为对应的命令。 然而,你可能想要执行外部命令,而不是一个 Git 子命令。 如果是那样的话,可以在命令前面加入 ! 符号。 如果你自己要写一些与 Git 仓库协作的工具的话,那会很有用。 我们现在演示将 git visual 定义为 gitk 的别名:
$ git config --global alias.visual '!gitk'
第二部分:Git 的杀手级特性:分支模型。
1, 分支简介(重要,对于理解很有帮助)
Git保存的不是文件的变化或者差异,而是一系列不同时刻的文件快照。
在进行暂存操作时,git会为每一个文件计算 校验和(即指纹) ,然后会把当前版本的文件快照保存到Git仓库中(Git使用blob对象来 保存它们),最终将 校验和 加入到暂存区域等待提交;
在进行提交操作时,Git会先计算项目根目录及其每一个子目录的 校验和, 然后在Git仓库中将这个 校验和 保存为 树对象。随后,Git便会创建一个提交对象,它包含了①作者的姓名,邮箱,提交时输入的信息②指向 前面叙述过的 树对象 的指针。如此,Git就可以在需要的时候重现此次保存的快照③指向 它的父对象的指针(第一次提交时没有)。
形象说明一下,现在假设有一个工作目录,里面包含了3个文件,然后执行如下操作:
$ git add README test.rb LICENSE
$ git commit -m 'The initial commit of my project'现在Git仓库里有五个对象:三个blob对象(保存着文件快照)、一个树对象(记录着目录结构和blob对象索引)以及一个提交对象(包含着指向前述树对象的指针和所有提交信息)。如下图所示:
做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针,如下图所示:
Git的分支,其实本质上仅仅是指向提交对象的可变指针。Git默认分支名字是master。每次提交操作中,分支指针都会自动向前移动。如下图所示:
2, 分支创建
git branch //查看分支
git branch -a//查看所有分支
git branch new_branch //基于当前分支的最新提交创建一个新的分支,新分支名称叫newBranch
git branch new_branch existing_branch //无论现在在哪个分支上,此命令会基于existing_branch的最新提交创建一个新分支。
git branch new_branch [commit_id] //无论现在在哪个分支上,此命令会基于某个提交(可以是当前分支的某个提交,也可以是其他任意一个分支上的某个提交ID)创建一个新分支。
Git 是怎么创建新分支的呢? 很简单,它只是为你创建了一个可以移动的新的指针。 比如,创建一个 testing 分支, 你需要使用 git branch 命令:
$ git branch testing
这会在当前所在的提交对象上创建一个指针。如下图所示:
那么,Git 又是怎么知道当前在哪一个分支上呢? 也很简单,它有一个名为
HEAD
的特殊指针。 请注意它和许多其它版本控制系统(如 Subversion 或 CVS)里的HEAD
概念完全不同。 在 Git 中,它是一个指针,指向当前所在的本地分支(译注:将HEAD
想象为当前分支的别名)。 在本例中,你仍然在master
分支上。 因为git branch
命令仅仅 创建 一个新分支,并不会自动切换到新分支中去。如下图所示:你可以简单地使用
git log
命令查看各个分支当前所指的对象。 提供这一功能的参数是--decorate
。
3, 分支切换
要切换到一个已存在的分支,你需要使用 git checkout 命令。 我们现在切换到新创建的 testing 分支去:
$ git checkout testing
这样 HEAD 就指向 testing 分支了。如下图所示:这么做的好处是什么呢?现在不妨做些改动再提交一次,如下图所示:
如图所示,你的
testing
分支向前移动了,但是master
分支却没有,它仍然指向运行git checkout
时所指的对象。 这就有意思了,现在我们切换回master
分支看看:这条命令做了两件事。 一是使 HEAD 指回
master
分支,二是将工作目录恢复成master
分支所指向的快照内容。 也就是说,你现在做修改的话,项目将始于一个较旧的版本。 本质上来讲,这就是忽略testing
分支所做的修改,以便于向另一个方向进行开发。
Note 分支切换会改变你工作目录中的文件在切换分支时,一定要注意你工作目录里的文件会被改变。 如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。 如果 Git 不能干净利落地完成这个任务,它将禁止切换分支。
我们不妨再稍微做些修改并提交。
现在,这个项目的提交历史已经产生了分叉(参见 项目分叉历史)。 因为刚才你创建了一个新分支,并切换过去进行了一些工作,随后又切换回 master 分支进行了另外一些工作。 上述两次改动针对的是不同分支:你可以在不同分支间不断地来回切换和工作,并在时机成熟时将它们合并起来。 而所有这些工作,你需要的命令只有 branch、checkout 和 commit。
你可以简单地使用
git log
命令查看分叉历史。 运行git log --oneline --decorate --graph --all
,它会输出你的提交历史、各个分支的指向以及项目的分支分叉情况。由于 Git 的分支实质上仅是包含所指提交对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁都异常高效。 创建一个新分支就相当于往一个文件中写入 41 个字节(40 个字符和 1 个换行符),如此的简单能不快吗?
这与过去大多数版本控制系统形成了鲜明的对比,它们在创建分支时,将所有的项目文件都复制一遍,并保存到一个特定的目录。 完成这样繁琐的过程通常需要好几秒钟,有时甚至需要好几分钟。所需时间的长短,完全取决于项目的规模。而在 Git 中,任何规模的项目都能在瞬间创建新分支。 同时,由于每次提交都会记录父对象,所以寻找恰当的合并基础(译注:即共同祖先)也是同样的简单和高效。 这些高效的特性使得 Git 鼓励开发人员频繁地创建和使用分支。
4, 分支的新建与合并
让我们来看一个简单的分支新建与分支合并的例子,实际工作中你可能会用到类似的工作流。 你将经历如下步骤:
- 开发某个网站。
- 为实现某个新的需求,创建一个分支。
- 在这个分支上开展工作。
正在此时,你突然接到一个电话说有个很严重的问题需要紧急修补。 你将按照如下方式来处理:
- 切换到你的线上分支(production branch)。
- 为这个紧急任务新建一个分支,并在其中修复它。
- 在测试通过之后,切换回线上分支,然后合并这个修补分支,最后将改动推送到线上分支。
- 切换回你最初工作的分支上,继续工作
新建分支
首先,我们假设你正在你的项目上工作,并且已经有一些提交。
现在,你已经决定要解决你的公司使用的问题追踪系统中的 #53 问题。 想要新建一个分支并同时切换到那个分支上,你可以运行一个带有
-b
参数的git checkout
命令:
$ git checkout -b iss53 Switched to a new branch "iss53"
它是下面两条命令的简写:
$ git branch iss53 $ git checkout iss53
你继续在 #53 问题上工作,并且做了一些提交。 在此过程中,
iss53
分支在不断的向前推进,因为你已经检出到该分支(也就是说,你的HEAD
指针指向了iss53
分支)
$ vim index.html $ git commit -a -m 'added a new footer [issue 53]'
现在你接到那个电话,有个紧急问题等待你来解决。 有了 Git 的帮助,你不必把这个紧急问题和
iss53
的修改混在一起,你也不需要花大力气来还原关于 53# 问题的修改,然后再添加关于这个紧急问题的修改,最后将这个修改提交到线上分支。 你所要做的仅仅是切换回master
分支。但是,在你这么做之前,要留意你的工作目录和暂存区里那些还没有被提交的修改,它可能会和你即将检出的分支产生冲突从而阻止 Git 切换到该分支。 最好的方法是,在你切换分支之前,保持好一个干净的状态。 有一些方法可以绕过这个问题(即,保存进度(stashing) 和 修补提交(commit amending)),我们会在 储藏与清理 中看到关于这两个命令的介绍。 现在,我们假设你已经把你的修改全部提交了,这时你可以切换回
master
分支了:
$ git checkout master Switched to branch 'master'
这个时候,你的工作目录和你在开始 #53 问题之前一模一样,现在你可以专心修复紧急问题了。 请牢记:当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。 Git 会自动添加、删除、修改文件以确保此时你的工作目录和这个分支最后一次提交时的样子一模一样。
接下来,你要修复这个紧急问题。 让我们建立一个针对该紧急问题的分支(hotfix branch),在该分支上工作直到问题解决:
$ git checkout -b hotfix Switched to a new branch 'hotfix' $ vim index.html $ git commit -a -m 'fixed the broken email address' [hotfix 1fb7853] fixed the broken email address 1 file changed, 2 insertions(+)
你可以运行你的测试,确保你的修改是正确的,然后将其合并回你的
master
分支来部署到线上。 你可以使用git merge
命令来达到上述目的:
$ git checkout master $ git merge hotfix Updating f42c576..3a0874c Fast-forward index.html | 2 ++ 1 file changed, 2 insertions(+)
在合并的时候,你应该注意到了"快进(fast-forward)"这个词。 由于当前
master
分支所指向的提交是你当前提交(有关 hotfix 的提交)的直接上游,所以 Git 只是简单的将指针向前移动。 换句话说,当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。现在,最新的修改已经在
master
分支所指向的提交快照中,你可以着手发布该修复了。关于这个紧急问题的解决方案发布之后,你准备回到被打断之前时的工作中。 然而,你应该先删除
hotfix
分支,因为你已经不再需要它了 ——master
分支已经指向了同一个位置。 你可以使用带-d
选项的git branch
命令来删除分支:
$ git branch -d hotfix Deleted branch hotfix (3a0874c).
现在你可以切换回你正在工作的分支继续你的工作,也就是针对 #53 问题的那个分支(iss53 分支)。
$ git checkout iss53 Switched to branch "iss53" $ vim index.html $ git commit -a -m 'finished the new footer [issue 53]' [iss53 ad82d7a] finished the new footer [issue 53] 1 file changed, 1 insertion(+)
你在
hotfix
分支上所做的工作并没有包含到iss53
分支中。 如果你需要拉取hotfix
所做的修改,你可以使用git merge master
命令将master
分支合并入iss53
分支,或者你也可以等到iss53
分支完成其使命,再将其合并回master
分支。分支的合并
假设你已经修正了 #53 问题,并且打算将你的工作合并入
master
分支。 为此,你需要合并iss53
分支到master
分支,这和之前你合并hotfix
分支所做的工作差不多。 你只需要检出到你想合并入的分支,然后运行git merge
命令:
$ git checkout master Switched to branch 'master' $ git merge iss53 Merge made by the 'recursive' strategy. index.html | 1 + 1 file changed, 1 insertion(+)
这和你之前合并
hotfix
分支的时候看起来有一点不一样。 在这种情况下,你的开发历史从一个更早的地方开始分叉开来(diverged)。 因为,master
分支所在提交并不是iss53
分支所在提交的直接祖先,Git 不得不做一些额外的工作。 出现这种情况的时候,Git 会使用两个分支的末端所指的快照(C4
和C5
)以及这两个分支的工作祖先(C2
),做一个简单的三方合并。和之前将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。 这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。
需要指出的是,Git 会自行决定选取哪一个提交作为最优的共同祖先,并以此作为合并的基础;这和更加古老的 CVS 系统或者 Subversion (1.5 版本之前)不同,在这些古老的版本管理系统中,用户需要自己选择最佳的合并基础。 Git 的这个优势使其在合并操作上比其他系统要简单很多。
既然你的修改已经合并进来了,你已经不再需要
iss53
分支了。 现在你可以在任务追踪系统中关闭此项任务,并删除这个分支。
$ git branch -d iss53
遇到冲突时的分支合并
有时候合并操作不会如此顺利。 如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们。 如果你对 #53 问题的修改和有关
hotfix
的修改都涉及到同一个文件的同一处,在合并它们的时候就会产生合并冲突:
$ git merge iss53 Auto-merging index.html CONFLICT (content): Merge conflict in index.html Automatic merge failed; fix conflicts and then commit the result.
此时 Git 做了合并,但是没有自动地创建一个新的合并提交。 Git 会暂停下来,等待你去解决合并产生的冲突。 你可以在合并冲突后的任意时刻使用
git status
命令来查看那些因包含合并冲突而处于未合并(unmerged)状态的文件:
$ git status On branch master You have unmerged paths. (fix conflicts and run "git commit") Unmerged paths: (use "git add
..." to mark resolution) both modified: index.html no changes added to commit (use "git add" and/or "git commit -a") 任何因包含合并冲突而有待解决的文件,都会以未合并状态标识出来。 Git 会在有冲突的文件中加入标准的冲突解决标记,这样你可以打开这些包含冲突的文件然后手动解决冲突。 出现冲突的文件会包含一些特殊区段,看起来像下面这个样子:
<<<<<<< HEAD:index.html
======= >>>>>>> iss53:index.html这表示
HEAD
所指示的版本(也就是你的master
分支所在的位置,因为你在运行 merge 命令的时候已经检出到了这个分支)在这个区段的上半部分(=======
的上半部分),而iss53
分支所指示的版本在=======
的下半部分。 为了解决冲突,你必须选择使用由=======
分割的两部分中的一个,或者你也可以自行合并这些内容。 例如,你可以通过把这段内容换成下面的样子来解决冲突:
上述的冲突解决方案仅保留了其中一个分支的修改,并且
<<<<<<<
,=======
, 和>>>>>>>
这些行被完全删除了。 在你解决了所有文件里的冲突之后,对每个文件使用git add
命令来将其标记为冲突已解决。 一旦暂存这些原本有冲突的文件,Git 就会将它们标记为冲突已解决。如果你想使用图形化工具来解决冲突,你可以运行
git mergetool
,该命令会为你启动一个合适的可视化合并工具,并带领你一步一步解决这些冲突:
$ git mergetool This message is displayed because 'merge.tool' is not configured. See 'git mergetool --tool-help' or 'git help config' for more details. 'git mergetool' will now attempt to use one of the following tools: opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge ecmerge p4merge araxis bc3 codecompare vimdiff emerge Merging: index.html Normal merge conflict for 'index.html': {local}: modified file {remote}: modified file Hit return to start merge resolution tool (opendiff):
如果你想使用除默认工具(在这里 Git 使用
opendiff
做为默认的合并工具,因为作者在 Mac 上运行该程序)外的其他合并工具,你可以在 “下列工具中(one of the following tools)” 这句后面看到所有支持的合并工具。 然后输入你喜欢的工具名字就可以了。
Note如果你需要更加高级的工具来解决复杂的合并冲突,我们会在 高级合并 介绍更多关于分支合并的内容。
等你退出合并工具之后,Git 会询问刚才的合并是否成功。 如果你回答是,Git 会暂存那些文件以表明冲突已解决: 你可以再次运行
git status
来确认所有的合并冲突都已被解决:
$ git status On branch master All conflicts fixed but you are still merging. (use "git commit" to conclude merge) Changes to be committed: modified: index.html
如果你对结果感到满意,并且确定之前有冲突的的文件都已经暂存了,这时你可以输入
git commit
来完成合并提交。 默认情况下提交信息看起来像下面这个样子:
Merge branch 'iss53' Conflicts: index.html # # It looks like you may be committing a merge. # If this is not correct, please remove the file # .git/MERGE_HEAD # and try again. # Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # All conflicts fixed but you are still merging. # # Changes to be committed: # modified: index.html #
如果你觉得上述的信息不够充分,不能完全体现分支合并的过程,你可以修改上述信息,添加一些细节给未来检视这个合并的读者一些帮助,告诉他们你是如何解决合并冲突的,以及理由是什么。
linux下git 差异对比、冲突解决图像化工具:beyond-compare【这个也是windows下小乌龟选择使用的工具】
1, 安装与破解:https://github.com/jyma1991/Crack-Beyond-Compare-linux
# Crack-Beyond-Compare-linux [crack beyond compare 4 on linux](https://github.com/jyma1991/Crack-Beyond-Compare-linux.git) ## 1.官网下载最新版 Beyond Compare 4 并安装 * [Beyond Compare 4](http://www.scootersoftware.com/download.php) 安装命令: ```vim sudo apt install -i bcompare-4.2.3.22587_amd64.deb ``` 如果报错 “依赖关系问题 - 仍未被配置” 使用如下命令 ```vim sudo apt-get install -f ``` 等分析完之后重新执行安装命令 ## 2.破解 ```vim cd /usr/lib/beyondcompare/ sudo sed -i "s/keexjEP3t4Mue23hrnuPtY4TdcsqNiJL-5174TsUdLmJSIXKfG2NGPwBL6vnRPddT7tH29qpkneX63DO9ECSPE9rzY1zhThHERg8lHM9IBFT+rVuiY823aQJuqzxCKIE1bcDqM4wgW01FH6oCBP1G4ub01xmb4BGSUG6ZrjxWHJyNLyIlGvOhoY2HAYzEtzYGwxFZn2JZ66o4RONkXjX0DF9EzsdUef3UAS+JQ+fCYReLawdjEe6tXCv88GKaaPKWxCeaUL9PejICQgRQOLGOZtZQkLgAelrOtehxz5ANOOqCaJgy2mJLQVLM5SJ9Dli909c5ybvEhVmIC0dc9dWH+/N9KmiLVlKMU7RJqnE+WXEEPI1SgglmfmLc1yVH7dqBb9ehOoKG9UE+HAE1YvH1XX2XVGeEqYUY-Tsk7YBTz0WpSpoYyPgx6Iki5KLtQ5G-aKP9eysnkuOAkrvHU8bLbGtZteGwJarev03PhfCioJL4OSqsmQGEvDbHFEbNl1qJtdwEriR+VNZts9vNNLk7UGfeNwIiqpxjk4Mn09nmSd8FhM4ifvcaIbNCRoMPGl6KU12iseSe+w+1kFsLhX+OhQM8WXcWV10cGqBzQE9OqOLUcg9n0krrR3KrohstS9smTwEx9olyLYppvC0p5i7dAx2deWvM1ZxKNs0BvcXGukR+/g" BCompare ``` ## 3.此时BCompare文件已被破解,打开软件会提示“Trial Mode Error!”表示成功,输入下面TEAM ZWT生成的密钥即可注册成功 ```nohighlight --- BEGIN LICENSE KEY --- GXN1eh9FbDiX1ACdd7XKMV7hL7x0ClBJLUJ-zFfKofjaj2yxE53xauIfkqZ8FoLpcZ0Ux6McTyNmODDSvSIHLYhg1QkTxjCeSCk6ARz0ABJcnUmd3dZYJNWFyJun14rmGByRnVPL49QH+Rs0kjRGKCB-cb8IT4Gf0Ue9WMQ1A6t31MO9jmjoYUeoUmbeAQSofvuK8GN1rLRv7WXfUJ0uyvYlGLqzq1ZoJAJDyo0Kdr4ThF-IXcv2cxVyWVW1SaMq8GFosDEGThnY7C-SgNXW30jqAOgiRjKKRX9RuNeDMFqgP2cuf0NMvyMrMScnM1ZyiAaJJtzbxqN5hZOMClUTE+++ --- END LICENSE KEY ----- ``` ## 4.成功后在目录~/.config/bcompare/下会生成文件BC4Key.txt 如下 ```nohighlight Beyond Compare 4 Licensed to: pwelyn Quantity: 9999 users Serial number: 9571-9981 License type: Pro Edition for Windows/Linux/OS X --- BEGIN LICENSE KEY --- GXN1eh9FbDiX1ACdd7XKMV7hL7x0ClBJLUJ-zFfKofjaj2yxE53xauIfk qZ8FoLpcZ0Ux6McTyNmODDSvSIHLYhg1QkTxjCeSCk6ARz0ABJcnUmd3d ZYJNWFyJun14rmGByRnVPL49QH+Rs0kjRGKCB-cb8IT4Gf0Ue9WMQ1A6t 31MO9jmjoYUeoUmbeAQSofvuK8GN1rLRv7WXfUJ0uyvYlGLqzq1ZoJAJD yo0Kdr4ThF-IXcv2cxVyWVW1SaMq8GFosDEGThnY7C-SgNXW30jqAOgiR jKKRX9RuNeDMFqgP2cuf0NMvyMrMScnM1ZyiAaJJtzbxqN5hZOMClUTE+ --- END LICENSE KEY ----- ``` ## 5.为所有用户注册bcompare 命令 ```vim sudo cp ~/.config/bcompare/BC4Key.txt /etc/ ```
2, 然后配置.gitconfig文件:
[diff] tool = bc3 //设置git difftool的工具是bc3[beyond compare] [merge] tool = bc3 //设置git mergetool的工具是bc3 [difftool] prompt = false //打开前是否提示 [mergetool] prompt = false keepBackup = false //默认情况下,使用git mergetool合并分支时,总会产生以*.orig为扩展名的备份文件。将keepBackup设置为false即可禁止产生备份文件。
5,分支管理
1,常用的一些分支管理工具
git branch 命令不只是可以创建与删除分支。 如果不加任何参数运行它,会得到当前所有分支的一个列表:
$ git branch
iss53
* master
testing
注意 master 分支前的 * 字符:它代表现在检出的那一个分支(也就是说,当前 HEAD 指针所指向的分支)。 这意味着如果在这时候提交,master 分支将会随着新的工作向前移动。 如果需要查看每一个分支的最后一次提交,可以运行 git branch -v 命令:$ git branch -v
iss53 93b412c fix javascript issue
* master 7a98805 Merge branch 'iss53'
testing 782fd34 add scott to the author list in the readmes
--merged 与 --no-merged 这两个有用的选项可以过滤这个列表中已经合并或尚未合并到当前分支的分支。 如果要查看哪些分支已经合并到当前分支,可以运行 git branch --merged:$ git branch --merged
iss53
* master
因为之前已经合并了 iss53 分支,所以现在看到它在列表中。 在这个列表中分支名字前没有 * 号的分支通常可以使用 git branch -d 删除掉;你已经将它们的工作整合到了另一个分支,所以并不会失去任何东西。查看所有包含未合并工作的分支,可以运行 git branch --no-merged:
$ git branch --no-merged
testing
这里显示了其他分支。 因为它包含了还未合并的工作,尝试使用 git branch -d 命令删除它时会失败:$ git branch -d testing
error: The branch 'testing' is not fully merged.
If you are sure you want to delete it, run 'git branch -D testing'.
如果真的想要删除分支并丢掉那些工作,如同帮助信息里所指出的,可以使用 -D 选项强制删除它。另外,git branch -d [brname]是无法删除当前正在检出的分支的。
6,分支开发工作流
在本节,我们会介绍一些常见的利用分支进行开发的工作流程。而正是由于分支管理的便捷,才衍生出这些典型的工作模式,你可以根据项目实际情况选择一种用用看。
6.1,长期分支
因为 Git 使用简单的三方合并,所以就算在一段较长的时间内,反复把一个分支合并入另一个分支,也不是什么难事。 也就是说,在整个项目开发周期的不同阶段,你可以同时拥有多个开放的分支;你可以定期地把某些特性分支合并入其他分支中。
许多使用 Git 的开发者都喜欢使用这种方式来工作,比如只在
master
分支上保留完全稳定的代码——有可能仅仅是已经发布或即将发布的代码。 他们还有一些名为develop
或者next
的平行分支,被用来做后续开发或者测试稳定性——这些分支不必保持绝对稳定,但是一旦达到稳定状态,它们就可以被合并入master
分支了。 这样,在确保这些已完成的特性分支(短期分支,比如之前的iss53
分支)能够通过所有测试,并且不会引入更多 bug 之后,就可以合并入主干分支中,等待下一次的发布。事实上我们刚才讨论的,是随着你的提交而不断右移的指针。 稳定分支的指针总是在提交历史中落后一大截,而前沿分支的指针往往比较靠前。
通常把他们想象成流水线(work silos)可能更好理解一点,那些经过测试考验的提交会被遴选到更加稳定的流水线上去。
你可以用这种方法维护不同层次的稳定性。 一些大型项目还有一个
proposed
(建议) 或pu: proposed updates
(建议更新)分支,它可能因包含一些不成熟的内容而不能进入next
或者master
分支。 这么做的目的是使你的分支具有不同级别的稳定性;当它们具有一定程度的稳定性后,再把它们合并入具有更高级别稳定性的分支中。 再次强调一下,使用多个长期分支的方法并非必要,但是这么做通常很有帮助,尤其是当你在一个非常庞大或者复杂的项目中工作时。6.2,特性分支
特性分支对任何规模的项目都适用。 特性分支是一种短期分支,它被用来实现单一特性或其相关工作。 也许你从来没有在其他的版本控制系统(
VCS
)上这么做过,因为在那些版本控制系统中创建和合并分支通常很费劲。 然而,在 Git 中一天之内多次创建、使用、合并、删除分支都很常见。你已经在上一节中你创建的
iss53
和hotfix
特性分支中看到过这种用法。 你在上一节用到的特性分支(iss53
和hotfix
分支)中提交了一些更新,并且在它们合并入主干分支之后,你又删除了它们。 这项技术能使你快速并且完整地进行上下文切换(context-switch)——因为你的工作被分散到不同的流水线中,在不同的流水线中每个分支都仅与其目标特性相关,因此,在做代码审查之类的工作的时候就能更加容易地看出你做了哪些改动。 你可以把做出的改动在特性分支中保留几分钟、几天甚至几个月,等它们成熟之后再合并,而不用在乎它们建立的顺序或工作进度。考虑这样一个例子,你在
master
分支上工作到C1
,这时为了解决一个问题而新建iss91
分支,在iss91
分支上工作到C4
,然而对于那个问题你又有了新的想法,于是你再新建一个iss91v2
分支试图用另一种方法解决那个问题,接着你回到master
分支工作了一会儿,你又冒出了一个不太确定的想法,你便在C10
的时候新建一个dumbidea
分支,并在上面做些实验。 你的提交历史看起来像下面这个样子:
现在,我们假设两件事情:你决定使用第二个方案来解决那个问题,即使用在
iss91v2
分支中方案;另外,你将dumbidea
分支拿给你的同事看过之后,结果发现这是个惊人之举。 这时你可以抛弃iss91
分支(即丢弃C5
和C6
提交),然后把另外两个分支合并入主干分支。 最终你的提交历史看起来像下面这个样子:
我们将会在 分布式 Git 中向你揭示更多有关分支工作流的细节,因此,请确保你阅读完那个章节之后,再来决定你的下个项目要使用什么样的分支策略(branching scheme)。
请牢记,当你做这么多操作的时候,这些分支全部都存于本地。 当你新建和合并分支的时候,所有这一切都只发生在你本地的 Git 版本库中 —— 没有与服务器发生交互。
7 ,远程分支
移到上面去
8,变基 (rebase) : 重新设置基底
参考文章:https://backlog.com/git-tutorial/tw/stepup/stepup2_8.html
http://gitbook.liuhui998.com/4_2.html
8.1,变基的含义理解:
假设一个主分支master在提交C3的地方新开辟了一个分支test,然后test分支修改提交了一次C4,然后返回mater分支修改提交了一次C5。变基就是指将一个分支(比如test)中的异于另一个分支(比如master)中的所有提交(即C4)按原有次序应用到另一个分支上(拼接到最新的地方之后,即C3-->C5-->C4)。变基好之后两个分支便是在同一条串线上的了(test在新maser在旧,原来test上的提交都被消除了,master依然在原来的地方上),合并便可以fast-forward了。这么做的好处是提交历史没有分支了(某个提交有两个父提交的情况不存在了)。具体代码如下:
- git checkout test //切换到test分支
- git rebase master //将当前分支(test)变基到基底(master)分支,此处可能有冲突,需要手动解决,解决之后用git add标记冲突已解决(add后不要commit),然后用git rebase --continue命令 继续rebase。这一步的具体实现过程如下:将test 分支里的每个提交取消掉,并且把他们临时保存为补丁(patch,这些补丁放在.git/rebase 目录中),然后把test分支更新到最新的maser分支(这里用master的最新的commitID也可以,而且更显示了这个操作的本质),最后把保存的这些补丁应用到 test分支上。
- git checkout master //切换到master分支
- git merge test //将test分支合并到当前的master分支上,模式为fast-forward
第二步之后的log情况如下:C3-->C5(master的HEAD指向此处)-->C4(test的HEAD指向此处)。第4步fast-forward将master的HEAD前移到C4,和test分支的HEAD指向同一个commit(C4)。
关于第二点说明一下,若有冲突,git add 标记冲突解决后,其实可以commit。如果不提交直接使用git rebase --continue,git会自己使用原来的提交信息提交掉,然后应用下一个补丁;如果自己手动提交,再使用git rebase --continue会提示你当前的这个补丁没有什么可以做的(也就是暂存区没有内容,其实你已经手动打上这个补丁了),所以此时你需要使用git rebase --skip来忽略这个补丁以使git继续应用下一个补丁。这个--continue和交互式里的--continue是不太一样的。
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=,
8.2 git rebase master test
将test分支变基到master分支上,相当于两个命令:①git checkout test ;②git rebase master。执行完后,当前分支会变为test(无论开始时是在哪个分支上)。
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
8.3, 参看下图,master分支在C2位置分出一个server分支,server分支上的C3提交处又分出clinet分支。现在的需求是将只属于client的提交(即C8,C9)合并到master分支上。方法如下:
git rebase --onto master server client
这个命令的意思是:取出client 分支(即git checkout client,会自动切换到client分支),找到client分支和server分支的共同祖先之后的修改,然后把它们在master分支上重放一遍(即合并到master分支上)。然后就可以切回到master分支,合并client分支(fast-forward)。
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==================---------------------------==========
8.4, 交互式变基(git rebase -i 或git rebase --interactive )
交互式使用git rebase过程不好叙述,自己去慢慢体验。说一点交互式的时候
- ①使用git rebase --continue实际就是使用git commit --amend,然后继续下一个操作;
- ②如果修改了之后自己手动提交则会在原来的那个提交上多一个提交,其余没什么区别。这个--continue和非交互式使用git rebase里的--continue是不太一样的。
另外,修改好某个commit之后再使用git rebase --continue的时候若有冲突,解决冲突,标记冲突然 后
- ①再使用git rebase --continue实际上是对原来那个commit更加崭新的一个commit上使用git commit --amend。
- ②解决冲突,标记冲突后 自己手动提交,和使用git rebase --continue是一样的效果,都是覆盖掉更加崭新的那个提交。
- ③解决冲突,标记冲突后 ,若手动使用git commit --amend 则更加崭新的那个提交会消失。还是不太理解其中的基本原理。先记着现象
实际上,在rebase之前的提交会以ORIG_HEAD之名存留。如果rebase之后无法复原到原先的状态,可以用git reset --hard ORIG_HEAD复原到rebase之前的状态。
8.5,变基的风险
不要对在你的仓库外有副本的分支执行变基。
如果你遵循这条金科玉律,就不会出差错。 否则,人民群众会仇恨你,你的朋友和家人也会嘲笑你,唾弃你。
变基操作的实质是丢弃一些现有的提交,然后相应地新建一些内容一样但实际上不同的提交。 如果你已经将提交推送至某个仓库,而其他人也已经从该仓库拉取提交并进行了后续工作,此时,如果你用
git rebase
命令重新整理了提交并再次推送,你的同伴因此将不得不再次将他们手头的工作与你的提交进行整合,如果接下来你还要拉取并整合他们修改过的提交,事情就会变得一团糟。
第三部分:分布式Git
1, 向一个项目贡献
git diff --check 在git add前使用此命令可以检查令其他开发者恼怒的空白问题。如果git add过了,可以使用git diff --cached/--staged --check。
1.1, 私有小型团队(*就是现在的情况..)
※自己总结几句:
git pull 等于两个命令①git fetch ②git merge origin/master(在master分支上执行)
git push实际上就是在origin/master分支上执行git merge master,只有当是fast-forward模式时才允许push。git push 之后origin/master分支自动指向master的最前端提交。
下面是具体论述,好好看!https://git-scm.com/book/zh/v2/%E5%88%86%E5%B8%83%E5%BC%8F-Git-%E5%90%91%E4%B8%80%E4%B8%AA%E9%A1%B9%E7%9B%AE%E8%B4%A1%E7%8C%AE
你可能会遇到的最简单的配置是有一两个其他开发者的私有项目。 “私有” 在这个上下文中,意味着闭源 - 不可以从外面的世界中访问到。 你和其他的开发者都有仓库的推送权限。
在这个环境下,可以采用一个类似使用 Subversion 或其他集中式的系统时会使用的工作流程。 依然可以得到像离线提交、非常容易地新建分支与合并分支等高级功能,但是工作流程可以是很简单的;主要的区别是合并发生在客户端这边而不是在提交时发生在服务器那边。 让我们看看当两个开发者在一个共享仓库中一起工作时会是什么样子。 第一个开发者,John,克隆了仓库,做了改动,然后本地提交。 (为了缩短这些例子长度,协议信息已被替换为
...
。)# John's Machine $ git clone john@githost:simplegit.git Initialized empty Git repository in /home/john/simplegit/.git/ ... $ cd simplegit/ $ vim lib/simplegit.rb $ git commit -am 'removed invalid default value' [master 738ee87] removed invalid default value 1 files changed, 1 insertions(+), 1 deletions(-)
第二个开发者,Jessica,做了同样的事情 - 克隆仓库并提交了一个改动:
# Jessica's Machine $ git clone jessica@githost:simplegit.git Initialized empty Git repository in /home/jessica/simplegit/.git/ ... $ cd simplegit/ $ vim TODO $ git commit -am 'add reset task' [master fbff5bc] add reset task 1 files changed, 1 insertions(+), 0 deletions(-)
现在,Jessica 把她的工作推送到服务器上:
# Jessica's Machine $ git push origin master ... To jessica@githost:simplegit.git 1edee6b..fbff5bc master -> master
John 也尝试推送他的改动:
# John's Machine $ git push origin master To john@githost:simplegit.git ! [rejected] master -> master (non-fast forward) error: failed to push some refs to 'john@githost:simplegit.git'
不允许 John 推送是因为在同一时间 Jessica 已经推送了。 如果之前习惯于用 Subversion 那么理解这点特别重要,因为你会注意到两个开发者并没有编辑同一个文件。 尽管 Subversion 会对编辑的不同文件在服务器上自动进行一次合并,但 Git 要求你在本地合并提交。 John 必须抓取 Jessica 的改动并合并它们,才能被允许推送。
$ git fetch origin ... From john@githost:simplegit + 049d078...fbff5bc master -> origin/master
在这个时候,John 的本地仓库看起来像这样:
John 有一个引用指向 Jessica 推送上去的改动,但是他必须将它们合并入自己的工作中之后才能被允许推送。
$ git merge origin/master Merge made by recursive. TODO | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
合并进行地很顺利 - John 的提交历史现在看起来像这样:
现在,John 可以测试代码,确保它依然正常工作,然后他可以把合并的新工作推送到服务器上:
$ git push origin master ... To john@githost:simplegit.git fbff5bc..72bbc59 master -> master
最终,John 的提交历史看起来像这样:
在此期间,Jessica 在一个特性分支上工作。 她创建了一个称作
issue54
的特性分支并且在那个分支上做了三次提交。 她还没有抓取 John 的改动,所以她的提交历史看起来像这样:Jessica 想要与 John 同步,所以她进行了抓取操作:
# Jessica's Machine $ git fetch origin ... From jessica@githost:simplegit fbff5bc..72bbc59 master -> origin/master
那会同时拉取 John 推送的工作。 Jessica 的历史现在看起来像这样:
Jessica 认为她的特性分支已经准备好了,但是她想要知道必须合并什么进入她的工作才能推送。 她运行
git log
来找出:$ git log --no-merges issue54..origin/master commit 738ee872852dfaa9d6634e0dea7a324040193016 Author: John Smith
Date: Fri May 29 16:01:27 2009 -0700 removed invalid default value
issue54..origin/master
语法是一个日志过滤器,要求 Git 只显示所有在后面分支(在本例中是origin/master
)但不在前面分支(在本例中是issue54
)的提交的列表。 我们将会在 提交区间 中详细介绍这个语法。目前,我们可以从输出中看到有一个 John 生成的但是 Jessica 还没有合并入的提交。 如果她合并
origin/master
,也就是说将会修改她的本地工作的那个单个提交。现在,Jessica 可以合并她的特性工作到她的 master 分支,合并 John 的工作(
origin/master
)进入她的master
分支,然后再次推送回服务器。 首先,为了整合所有这些工作她切换回她的 master 分支。$ git checkout master Switched to branch 'master' Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
她既可以先合并
origin/master
也可以先合并issue54
- 它们都是上游,所以顺序并没有关系。 不论她选择的顺序是什么最终的结果快照是完全一样的;只是历史会有一点轻微的区别。 她选择先合并入issue54
:$ git merge issue54 Updating fbff5bc..4af4298 Fast forward README | 1 + lib/simplegit.rb | 6 +++++- 2 files changed, 6 insertions(+), 1 deletions(-)
没有发生问题;如你所见它是一次简单的快进。 现在 Jessica 合并入 John 的工作(
origin/master
):$ git merge origin/master Auto-merging lib/simplegit.rb Merge made by recursive. lib/simplegit.rb | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
每一个文件都干净地合并了,Jessica 的历史看起来像这样:
现在
origin/master
是可以从 Jessica 的master
分支到达的,所以她应该可以成功地推送(假设同一时间 John 并没有再次推送):$ git push origin master ... To jessica@githost:simplegit.git 72bbc59..8059c15 master -> master
每一个开发者都提交了几次并成功地合并了其他人的工作。
这是一个最简单的工作流程。 你通常在一个特性分支工作一会儿,当它准备好整合时合并回你的 master 分支。 当想要共享工作时,将其合并回你自己的 master 分支,如果有改动的话然后抓取并合并
origin/master
,最终推送到服务器上的master
分支。 通常顺序像这样:
2, 维护项目(作为管理者管理项目)
2.1,
git apply
git log branchA --not branchB //显示在branchA中在不在branchB中的提交。
git log branchB..branchA //和上面的效果一样,显示在后面分支但不在前面分支的提交。
git log -p //-p选项会显示每次提交所引入的差异
git diff branchA // 以branchA为基准,比较当前分支和branchA分支的差异。
git diff branchA branchB // 以branchA为基准,比较branchB和branchA的差异。
2.2 合并分支前,确定特性分支中引入的那些内容
实际上就是将特性分支(topic)的最新提交与两个分支的共同祖先提交进行比对。可以有两种方法:
①,手动找出公共祖先,然后比对
- git merge-base topic master //这个命令用于找到topic和master分支的共同祖先提交(以前就是两个分支合并的基准提交),这个命令会返回共同祖先的提交哈希值hash1。
- git diff hash1 //以hash1提交为基准,比较当前分支的最新提交与其的差异。
②,上面的做法比较麻烦,Git提供了一种比较便捷的方式:三点语法。
- git diff master...topic //该命令会将topic分支的最新提交与两个分支的共同祖先(假设为A)提交做比对,以A为基准,即git diff A topic
2.3 合并工作流
合并工作流有很多种方式,但其实质就是合并各个不同的分支。以下选取几个典型的工作流加以说明:
工作流1:release 分支是长期分支,用于发布。其余的分支在此基础上开发,开发分支完成后合并到release分支上去。这是最简单的一种方式,但风险也大。这种方式适合一个人开发,没有其他人干扰你的代码。
工作流2(中小型项目最常用):有两个长期分支:release和develop。release用于发布,develop用于测试,其余的topic分支用于开发。当开发完成后将各个topic分支合并入develop分支,测试没有问题之后再将develop合并入release分支(此时是fast-forward模式)。图形表示如下:
如图,master分支一直提交到C2,此时需要协同开发或者是需要开辟新的分支做些开发,在master的基础上检出develop分支用于发布前的测试,其余的topic开发分支也在master分支上checkout出来,以后各个topic分支开发完成后直接合并入develop分支,develop分支测试没问题之后再将master分支快进到develop的HEAD处。
工作流3:大项目工作流
就是分支更多了,有四个长期分支:
master
、next
,用于新工作的pu
(proposed updates)和用于维护性向后移植工作(maintenance backports)的maint
分支。具体不叙述了。2.4 变基与挑选工作流
①自己理解:
git cherry-pick commitID //此命令会将指定的提交commitID应用到当前分支上。有可能会有冲突,按提示操作即可。
上面这个操作也可以用变基完成,具体操作步骤如下:
假设情况如下面图80所示,同样只想把ruby_client分支中的e43a6提交提取出来合并到master分支,
- git rebase -i f42c5 //这是在ruby_client分支上执行的,此语句会将两者共同祖先之后的ruby_client分支上的所有提交显示出来,然后在编辑器里将5ddae删除就可以只将e43a6提交变基到master上,此时有冲突解决然后continue,然后将mater快进到ruby_client分支,然后删除ruby_client分支即可。
- 上面的命令有以下几个同义语句:
- git rebase -i f42c5 //ruby_client分支上执行
- git rebase -i master //ruby_client分支上执行
- git rebase -i master或f42c5 ruby_client //这个命令可以在任何分支上执行,执行过后会自动切换到ruby_client分支
②摘抄:
为了保持线性的提交历史,有些维护者更喜欢在 master 分支上对贡献过来的工作进行变基和拣选,而不是直接将其合并。变基前面说过了,下面说说挑选:
Git 中的拣选类似于对特定的某次提交的变基。 它会提取该提交的补丁,之后尝试将其重新应用到当前分支上。 这种方式在你只想引入特性分支中的某个提交,或者特性分支中只有一个提交,而你不想运行变基时很有用。 举个例子,假设你的项目提交历史类似:
如果你希望将提交
e43a6
拉取到 master 分支,你可以运行:$ git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdf Finished one cherry-pick. [master]: created a0a41a9: "More friendly message when locking the index fails." 3 files changed, 17 insertions(+), 3 deletions(-)
这样会拉去和 e43a6相同的更改,但是因为应用的日期不同,你会得到一个新的SHA-1值。
然后你就可以删除ruby_client这个特性分支,并丢弃不想拉入的提交。
2.5 为发布打标签 略
2.6 生成一个构建号
Git 中不存在随每次提交递增的“v123”之类的数字序列,如果你想要为提交附上一个可读的名称,可以对其运行
git describe
命令。 Git 将会给出一个字符串,它由最近的标签名、自该标签之后的提交数目和你所描述的提交的部分 SHA-1 值构成:
$ git describe master v1.6.2-rc1-20-g8c5b85c
这样你在导出一个快照或构建时,可以给出一个便于人们理解的命名。 实际上,如果你的 Git 是从 Git 自己的版本库克隆下来并构建的,那么
git --version
命令给出的结果是与此类似的。 如果你所描述的提交自身就有一个标签,那么它将只会输出标签名,没有后面两项信息。注意
git describe
命令只适用于有注解的标签(即使用-a
或-s
选项创建的标签),所以如果你在使用git describe
命令的话,为了确保能为标签生成合适的名称,打发布标签时都应该采用加注解的方式。 你也可以使用这个字符串来调用 checkout 或 show 命令,但是这依赖于其末尾的简短 SHA-1 值,因此不一定一直有效。 比如,最近 Linux 内核为了保证 SHA-1 值对象的唯一性,将其位数由 8 位扩展到了 10 位,导致以前的git describe
输出全部失效。2.7 准备一次发布
现在你可以发布一个构建了。 其中一件事情就是为那些不使用 Git 的可怜包们创建一个最新的快照归档。 使用
git archive
命令完成此工作:
$ git archive master --prefix='project/' | gzip > `git describe master`.tar.gz $ ls *.tar.gz v1.6.2-rc1-20-g8c5b85c.tar.gz
将项目中的所有文件放入一个名为 --prefix描述的文件夹内。
如果有人将这个压缩包解压,他就可以得到你的项目文件夹的最新快照。 你也可以以类似的方式创建一个 zip 压缩包,但此时你应该向
git archive
命令传递--format=zip
选项:
$ git archive master --prefix='project/' --format=zip > `git describe master`.zip
现在你有了本次发布的一个 tar 包和一个 zip 包,可以将其上传到网站或以电子邮件的形式发送给人们。
2.8 制作提交简报
现在是时候通知邮件列表里那些好奇你的项目发生了什么的人了。 使用
git shortlog
命令可以快速生成一份包含从上次发布之后项目新增内容的修改日志(changelog)类文档。 它会对你给定范围内的所有提交进行总结;比如,你的上一次发布名称是 v1.0.1,那么下面的命令可以给出上次发布以来所有提交的总结:$ git shortlog --no-merges master --not v1.0.1 Chris Wanstrath (8): Add support for annotated tags to Grit::Tag Add packed-refs annotated tag support. Add Grit::Commit#to_patch Update version and History.txt Remove stray `puts` Make ls_tree ignore nils Tom Preston-Werner (4): fix dates in history dynamic version method Version bump to 1.0.2 Regenerated gemspec for version 1.0.2
这份整洁的总结包括了自 v1.0.1 以来的所有提交,并且已经按照作者分好组,你可以通过电子邮件将其直接发送到列表中。
第四部分,Github
第五部分, Git 工具
1 选择修订版本
git show
分支名可以代替这个分支的最后一个提交对象(的SHA-1)
git rev-parse branchname // 显示出指定的branch所指向的提交对象
1.1, 引用日志
当你在工作时, Git 会在后台保存一个引用日志(reflog),引用日志记录了最近几个月你的 HEAD 和分支引用所指向的历史。
你可以使用
git reflog
来查看引用日志$ git reflog 734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated d921970 HEAD@{1}: merge phedders/rdocs: Merge made by recursive. 1c002dd HEAD@{2}: commit: added some blame and merge stuff 1c36188 HEAD@{3}: rebase -i (squash): updating HEAD 95df984 HEAD@{4}: commit: # This is a combination of two commits. 1c36188 HEAD@{5}: rebase -i (squash): updating HEAD 7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD
每当你的 HEAD 所指向的位置发生了变化,Git 就会将这个信息存储到引用日志这个历史记录里。 通过这些数据,你可以很方便地获取之前的提交历史。 如果你想查看仓库中 HEAD 在五次前的所指向的提交,你可以使用
@{n}
来引用 reflog 中输出的提交记录。$ git show HEAD@{5}
你同样可以使用这个语法来查看某个分支在一定时间前的位置。 例如,查看你的
master
分支在昨天的时候指向了哪个提交,你可以输入$ git show master@{yesterday}
就会显示昨天该分支的顶端指向了哪个提交。 这个方法只对还在你引用日志里的数据有用,所以不能用来查好几个月之前的提交。
可以运行
git log -g
来查看类似于git log
输出格式的引用日志信息:$ git log -g master commit 734713bc047d87bf7eac9674765ae793478c50d3 Reflog: master@{0} (Scott Chacon
) Reflog message: commit: fixed refs handling, added gc auto, updated Author: Scott Chacon Date: Fri Jan 2 18:32:33 2009 -0800 fixed refs handling, added gc auto, updated tests commit d921970aadf03b3cf0e71becdaab3147ba71cdef Reflog: master@{1} (Scott Chacon ) Reflog message: merge phedders/rdocs: Merge made by recursive. Author: Scott Chacon Date: Thu Dec 11 15:08:43 2008 -0800 Merge commit 'phedders/rdocs' 值得注意的是,引用日志只存在于本地仓库,一个记录你在你自己的仓库里做过什么的日志。 其他人拷贝的仓库里的引用日志不会和你的相同;而你新克隆一个仓库的时候,引用日志是空的,因为你在仓库里还没有操作。
git show HEAD@{2.months.ago}
这条命令只有在你克隆了一个项目至少两个月时才会有用——如果你是五分钟前克隆的仓库,那么它将不会有结果返回。1.2,祖先引用
HEAD^表示"HEAD的父提交",当然也可以用commitID^
HEAD^2表示"HEAD的的第二父提交", 这个语法只适用于合并(merge)的提交,因为合并提交会有多个父提交。第一父提交是你合并时所在分支,第二父提交是你所合并的分支。
HEAD~同样指向第一父提交,因此HEAD~和HEAD^是等价的。
HEAD~2(也可用HEAD~~)表示第一父提交的第一父提交,也就是祖父提交。等价于HEAD^^,即第一父提交的第一父提交。
HEAD~3^2表示HEAD的第三个第一个父提交的第二父提交.
1.3, 提交区间(即一次可以选择到多个提交对象)
你已经学会如何指定单次的提交,现在来看看如何指明一定区间的提交。 当你有很多分支时,这对管理你的分支时十分有用,你可以用提交区间来解决 “这个分支还有哪些提交尚未合并到主分支?” 的问题
双点(..)
最常用的指明提交区间语法是双点。 这种语法可以让 Git 选出在一个分支中而不在另一个分支中的提交。 例如,你有如下的提交历史 Example history for range selection.
你想要查看 experiment 分支中还有哪些提交尚未被合并入 master 分支。 你可以使用
master..experiment
来让 Git 显示这些提交。也就是 “在 experiment 分支中而不在 master 分支中的提交”。 为了使例子简单明了,我使用了示意图中提交对象的字母来代替真实日志的输出,所以会显示:$ git log master..experiment D C
反过来,如果你想查看在
master
分支中而不在experiment
分支中的提交,你只要交换分支名即可。experiment..master
会显示在master
分支中而不在experiment
分支中的提交:$ git log experiment..master F E
这可以让你保持
experiment
分支跟随最新的进度以及查看你即将合并的内容。 另一个常用的场景是查看你即将推送到远端的内容:$ git log origin/master..HEAD
这个命令会输出在你当前分支中而不在远程
origin
中的提交。 如果你执行了git push
并且你的当前分支正在跟踪origin/master
,git log origin/master..HEAD
所输出的提交将会被传输到远端服务器。 如果你留空了其中的一边, Git 会默认为 HEAD。 例如,git log origin/master..
将会输出与之前例子相同的结果 —— Git 使用 HEAD 来代替留空的一边。
多点
双点语法很好用,但有时候你可能需要两个以上的分支才能确定你所需要的修订,比如查看哪些提交是被包含在某些分支中的一个,但是不在你当前的分支上。 Git 允许你在任意引用前加上
^
字符或者--not
来指明你不希望提交被包含其中的分支。 因此下列3个命令是等价的:$ git log refA..refB $ git log ^refA refB $ git log refB --not refA
这个语法很好用,因为你可以在查询中指定超过两个的引用,这是双点语法无法实现的。 比如,你想查看所有被
refA
或refB
包含的但是不被refC
包含的提交,你可以输入下面中的任意一个命令$ git log refA refB ^refC $ git log refA refB --not refC
这就构成了一个十分强大的修订查询系统,你可以通过它来查看你的分支里包含了哪些东西。
三点
[自注]:git log 与 git diff中的三点语法不太一样,git log 是显示两个分支中各自独有的提交,git diff是显示公共祖先 与指定的分支(三个点后面的那个分支)的差异
最后一种主要的区间选择语法是三点,这个语法可以选择出被两个引用中的一个包含但又不被两者同时包含的提交。 再看看之前双点例子中的提交历史。 如果你想看
master
或者experiment
中包含的但不是两者共有的提交,你可以执行$ git log master...experiment F E D C
这和通常
log
按日期排序的输出一样,仅仅给出了4个提交的信息。这种情形下,
log
命令的一个常用参数是--left-right
,它会显示每个提交到底处于哪一侧的分支。 这会让输出数据更加清晰。$ git log --left-right master...experiment < F < E > D > C
有了这些工具,你就可以十分方便地查看你 Git 仓库中的提交。
2 交互式暂存
git add -i //会进行交互式暂存,感觉没啥用,不用交互式也都能完成其功能。
git add -p [file....] 或 git add --patch [file....]可以交互式的做部分文件暂存,即一次只暂存一个文件中若干个部分改动,而不是全部改动。
更进一步地,可以使用
reset --patch
命令的补丁模式来部分重置文件,通过checkout --patch
命令来部分检出文件与stash save --patch
命令来部分暂存文件。 我们将会在接触这些命令的高级使用方法时了解更多详细信息。
3 储藏与清理
切换分支前,如果工作目录是脏的状态----即,工作区有未提交状态的文件(包括未暂存的和暂存了没有提交两种状态的文件)---是无法直接切换的。此时可以通过git stash 将脏的状态 保存在一个栈上,以后任何时间都可以重新应用(恢复)这些储藏。当然stash的应用场景不只是切换分支时。
git stash 或 git stash save // 储藏文件
git stash list //查看储藏的东西
恢复储藏可以在任何时间任何位置任何状态,有两种方法恢复(应用)储藏:
①
- git stash apply [stashID] [--index] //应用[可选]指定的stashID(形如stash@{n})到当前工作目录,如果不指定则应用最新的那个(即stash@{0})。--index选项是可选的,如果不加此选项,stash前若有暂存的文件,应用储藏后都会变为未暂存的状态,有了此选项则会正常恢复。
- git stash drop [stashID] //git stash apply只会尝试应用储藏,在堆栈上还有它。运行git stash drop 可以移除指定的储藏,不指定则移除最新的那个。
②git stash pop [--index] //这个命令相当于上面两个命令。即应用储藏然后立即从栈上扔掉它。
※储藏的变种(即加上各种参数)
git stash --keep-index // 此参数告诉Git不要储藏任何你通过git add命令已暂存的东西。注意,这里可以了解到一点:暂存区实际也是索引区,index和stage某种意义上在git中是同义的。
git stash --include-untracked(或-u参数) //默认情况下git stash只会储藏已经在索引(已经暂存过)中的的文件,加上这个 参数之后Git也会储藏任何创建的未跟踪文件。
git stash --patch //
--patch
标记,Git 不会储藏所有修改过的任何东西,但是会交互式地提示哪些改动想要储藏、哪些改动需要保存在工作目录中。git stash branch [branchname] //创建一个新的分支并检出储藏所在的分支并将储藏在新分支上应用,然后应用成功后扔掉储藏。这个命令应用前应该已经储藏过一些东西了。git branch name是在当前分支基础上创建一个新的分支(工作目录必须是干净的),git stash branch name也是这个功能,但是它创建的这个分支还包括工作区中的脏状态(首先要被stash起来)。
※清理工作目录
git clean
- 使用这个命令前一定要使用-n选项来“做一次演习然后告诉你 将要 移除什么”。
- 另一个小心处理过程的方式是使用
-i
或 “interactive”,这将会交互式的运行clean命令。- 你需要谨慎地使用这个命令,因为它被设计为从工作目录中移除未被追踪的文件。 如果你改变主意了,你也不一定能找回来那些文件的内容。 一个更安全的选项是运行
git stash --all
来移除每一样东西并存放在栈中。- -d选项可以删除未跟踪的文件夹
- -f选项 强制移除
- 默认情况下,
git clean
命令只会移除没有忽略的未跟踪文件。 任何与.gitiignore
或其他忽略文件中的模式匹配的文件都不会被移除。 如果你也想要移除那些文件,例如为了做一次完全干净的构建而移除所有由构建生成的.o
文件,可以给 clean 命令增加一个-x
选项。
4 签署工作(略)
gpg --gen-key
gpg --list-keys
git tag -s
git tag -v
5 搜索
无论仓库里的代码量有多少,你经常需要查找一个函数是在哪里调用或者定义的,或者一个方法的变更历史。 Git 提供了两个有用的工具来快速地从它的数据库中浏览代码和提交。
5.1, Git Grep
此命令可以很方便地从提交历史或者工作目录中查找一个字符串或者正则表达式。默认情况下 Git 会查找你工作目录的文件。一些常用选项参数如下:
- 你可以传入
-n
参数来输出 Git 所找到的匹配行行号。- 你可以使用
--count
选项来使 Git 输出概述的信息,仅仅包括哪些文件包含匹配以及每个文件包含了多少个匹配。- 如果你想看匹配的行是属于哪一个方法或者函数,你可以传入
-p
选项。相比于一些常用的搜索命令比如
grep
和ack
,git grep
命令有一些的优点。 第一就是速度非常快,第二是你不仅仅可以可以搜索工作目录,还可以搜索任意的 Git 树。5.2 Git日志搜索
5.2.1 或许你不想知道某一项在 哪里 ,而是想知道是什么 时候 存在或者引入的。
git log
命令有许多强大的工具可以通过提交信息甚至是 diff 的内容来找到某个特定的提交。例如,如果我们想找到
ZLIB_BUF_MAX
常量是什么时候引入的,我们可以使用-S
选项来显示新增和删除该字符串的提交。git log -SZLIB_BUF_MAX --oneline
如果你希望得到更精确的结果,你可以使用
-G
选项来使用正则表达式搜索。
5.2.2 行日志搜索
是另一个相当高级并且有用的日志搜索功能。 这是一个最近新增的不太知名的功能,但却是十分有用。 在
git log
后加上-L
选项即可调用,它可以展示代码中一行或者一个函数的历史。例如,假设我们想查看
zlib.c
文件中`git_deflate_bound` 函数的每一次变更,我们可以执行git log -L :git_deflate_bound:zlib.c
。 Git 会尝试找出这个函数的范围,然后查找历史记录,并且显示从函数创建之后一系列变更对应的补丁。$ git log -L :git_deflate_bound:zlib.c commit ef49a7a0126d64359c974b4b3b71d7ad42ee3bca Author: Junio C Hamano
Date: Fri Jun 10 11:52:15 2011 -0700 zlib: zlib can only process 4GB at a time diff --git a/zlib.c b/zlib.c --- a/zlib.c +++ b/zlib.c @@ -85,5 +130,5 @@ -unsigned long git_deflate_bound(z_streamp strm, unsigned long size) +unsigned long git_deflate_bound(git_zstream *strm, unsigned long size) { - return deflateBound(strm, size); + return deflateBound(&strm->z, size); } commit 225a6f1068f71723a910e8565db4e252b3ca21fa Author: Junio C Hamano Date: Fri Jun 10 11:18:17 2011 -0700 zlib: wrap deflateBound() too diff --git a/zlib.c b/zlib.c --- a/zlib.c +++ b/zlib.c @@ -81,0 +85,5 @@ +unsigned long git_deflate_bound(z_streamp strm, unsigned long size) +{ + return deflateBound(strm, size); +} + 如果 Git 无法计算出如何匹配你代码中的函数或者方法,你可以提供一个正则表达式。 例如,这个命令和上面的是等同的:
git log -L '/unsigned long git_deflate_bound/',/^}/:zlib.c
。 你也可以提供单行或者一个范围的行号来获得相同的输出。
6 重写历史
7 重置揭密
8 高级合并
9 Rerere
10 使用 Git 调试
11 子模块
12 打包
13 替换
14 凭证存储
15 总结
1