git学习笔记

教程网站:https://git-scm.com/book/zh/v2/  

 


 


*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 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的最新的共同节点为<common>, 然后本地提交了一个新的提交<local_1>,远程上有两个新的提交<remote_1>, <remote_2>. 然后利用git pull命令拉取,git pull的过程如下:把<remote_1>,<remote_2>拉取下来,然后把<local_1>放在<remote_2>的前面,然后再把<local_1>,<remote_1>, <remote_2>合并之后自动做一次提交放在<local_1>的前面。此时顺序是: <common><remote_1><remote_2><local_1><merge>.

 ※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 git@github.com:toyijiu/DailyBlog.git (fetch) 
  origin git@github.com:toyijiu/DailyBlog.git (push)

如果你没有拉取的仓库的ssh网址的密码,也可以使用下面的解决方法:

  git config --global credential.helper store

然后你会在你本地生成一个文本,上边记录你的账号和密码。然后你使用上述的命令配置好之后,再操作一次git pull,然后它会提示你输入账号密码,这一次之后就不需要再次输入密码了。


蒋鑫 Git权威指南学习笔记,这是一本手册性的书籍

☯ git config <section>.<key>可以读取配置配置文件中的配置信息

git config <section>.<key>   <value>可以设置配置文件,有一下三种级别的配置

  •  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 All
  • git add . stages new and modified, without deleted
  • git 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/za/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 <file>..." 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 将每个提交放在一行显示,查看的提交数很大时非常有用。 另外还有 shortfull 和 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> 选项的写法,其中的 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, 远程仓库的使用

git remote //查看每一个远程服务器(或者说远程仓库)的简写。如果说是克隆的仓库,至少有一个origin,这是git给克隆仓库服务器的默认名字。

git remote -v //查看简写及其对应的URL

git remote add <shortname> <url> //添加一个新的远程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 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  <tag> 命令可以看到标签信息与对应的提交信息。输出显示了打标签者的信息、打标签的日期时间、附注信息,然后显示具体的提交信息。

3.2, 轻量标签

另一种给提交打标签的方式是使用轻量标签。 轻量标签本质上是将提交校验和存储到一个文件中 - 没有保存任何其他信息。 创建轻量标签,不需要使用 -a-s 或 -m 选项,只需要提供标签名字:

$ git tag v1.4

这时,如果在标签上运行 git show <tag>,你不会看到额外的标签信息。 命令只会显示出提交信息。

3.3, 为过去的某次历史提交打标签

要在那个提交上打标签,你需要在命令的末尾指定提交的校验和(或部分校验和):

$ git tag -a v1.2  8fcdb32

3.4, 共享标签

※ 默认情况下,git push 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上。 这个过程就像共享远程分支一样 - 你可以运行 git push origin [tagname]  /  git push origin <branchName> [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 <branchName> -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)。
  • 为这个紧急任务新建一个分支,并在其中修复它。
  • 在测试通过之后,切换回线上分支,然后合并这个修补分支,最后将改动推送到线上分支。
  • 切换回你最初工作的分支上,继续工作

新建分支

首先,我们假设你正在你的项目上工作,并且已经有一些提交。

一个简单的提交历史。
Figure 18. 一个简单提交历史

现在,你已经决定要解决你的公司使用的问题追踪系统中的 #53 问题。 想要新建一个分支并同时切换到那个分支上,你可以运行一个带有 -b 参数的 git checkout 命令:

$ git checkout -b iss53
Switched to a new branch "iss53"

它是下面两条命令的简写:

$ git branch iss53
$ git checkout iss53
创建一个新分支指针。
Figure 19. 创建一个新分支指针

你继续在 #53 问题上工作,并且做了一些提交。 在此过程中,iss53 分支在不断的向前推进,因为你已经检出到该分支(也就是说,你的 HEAD 指针指向了 iss53 分支)

$ vim index.html
$ git commit -a -m 'added a new footer [issue 53]'
iss53 分支随着工作的进展向前推进。
Figure 20. iss53 分支随着工作的进展向前推进

现在你接到那个电话,有个紧急问题等待你来解决。 有了 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` 分支的紧急问题分支(hotfix branch)。
Figure 21. 基于 master 分支的紧急问题分支 hotfix branch

你可以运行你的测试,确保你的修改是正确的,然后将其合并回你的 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 分支所指向的提交快照中,你可以着手发布该修复了。

`master` 被快进到 `hotfix`。
Figure 22. master 被快进到 hotfix

关于这个紧急问题的解决方案发布之后,你准备回到被打断之前时的工作中。 然而,你应该先删除 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(+)
继续在 `iss53` 分支上的工作。
Figure 23. 继续在 iss53 分支上的工作

你在 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),做一个简单的三方合并。

一次典型合并中所用到的三个快照。
Figure 24. 一次典型合并中所用到的三个快照

和之前将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。 这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。

一个合并提交。
Figure 25. 一个合并提交

需要指出的是,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 <file>..." to mark resolution)

    both modified:      index.html

no changes added to commit (use "git add" and/or "git commit -a")

任何因包含合并冲突而有待解决的文件,都会以未合并状态标识出来。 Git 会在有冲突的文件中加入标准的冲突解决标记,这样你可以打开这些包含冲突的文件然后手动解决冲突。 出现冲突的文件会包含一些特殊区段,看起来像下面这个样子:

<<<<<<< HEAD:index.html
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
 please contact us at support@github.com
</div>
>>>>>>> iss53:index.html

这表示 HEAD 所指示的版本(也就是你的 master 分支所在的位置,因为你在运行 merge 命令的时候已经检出到了这个分支)在这个区段的上半部分(======= 的上半部分),而 iss53 分支所指示的版本在 ======= 的下半部分。 为了解决冲突,你必须选择使用由 ======= 分割的两部分中的一个,或者你也可以自行合并这些内容。 例如,你可以通过把这段内容换成下面的样子来解决冲突:

<div id="footer">
please contact us at email.support@github.com
</div>

上述的冲突解决方案仅保留了其中一个分支的修改,并且 <<<<<<< , ======= , 和 >>>>>>> 这些行被完全删除了。 在你解决了所有文件里的冲突之后,对每个文件使用 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/
```
View Code

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 之后,就可以合并入主干分支中,等待下一次的发布。

事实上我们刚才讨论的,是随着你的提交而不断右移的指针。 稳定分支的指针总是在提交历史中落后一大截,而前沿分支的指针往往比较靠前。

 

渐进稳定分支的线性图。
Figure 26. 渐进稳定分支的线性图

通常把他们想象成流水线(work silos)可能更好理解一点,那些经过测试考验的提交会被遴选到更加稳定的流水线上去。

渐进稳定分支的工作流(“silo”)视图。
Figure 27. 渐进稳定分支的流水线(“silo”)视图

你可以用这种方法维护不同层次的稳定性。 一些大型项目还有一个 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 分支,并在上面做些实验。 你的提交历史看起来像下面这个样子:

 

拥有多个特性分支的提交历史。
Figure 28. 拥有多个特性分支的提交历史

现在,我们假设两件事情:你决定使用第二个方案来解决那个问题,即使用在 iss91v2 分支中方案;另外,你将 dumbidea 分支拿给你的同事看过之后,结果发现这是个惊人之举。 这时你可以抛弃 iss91 分支(即丢弃 C5 和 C6 提交),然后把另外两个分支合并入主干分支。 最终你的提交历史看起来像下面这个样子:

 

合并了 `dumbidea` 和 `iss91v2` 分支之后的提交历史。
Figure 29. 合并了 dumbidea 和 iss91v2 分支之后的提交历史

我们将会在 分布式 Git 中向你揭示更多有关分支工作流的细节,因此,请确保你阅读完那个章节之后,再来决定你的下个项目要使用什么样的分支策略(branching scheme)。

请牢记,当你做这么多操作的时候,这些分支全部都存于本地。 当你新建和合并分支的时候,所有这一切都只发生在你本地的 Git 版本库中 —— 没有与服务器发生交互。


7 ,远程分支

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] 【本地和远程分支名称可以不同.如果远程还没有相应分支,则会自动创建名称为remote_branch的分支】

git push -u [remote] [local_branch] : [remote_branch]   -u 标记;这是 --set-upstream 的简写,设置上游分支(上游分支是指以本地的origin/master代表的远程的master分支, origin/master是本地的远程分支, 可能与远程仓库不一样,通过git pull origin master 与远程仓库保持同步。),即将remote_branch与本地的local_branch关联起来,只需要在第一次推送时使用。该标记会为之后轻松地推送与拉取配置分支。

git fetch origin //

git pull // 相当于git fetch 和git merge两个命令

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 pull拉取一下,将新的远程分支拉取到本地,然后在执行上述命令。

git branch -u [remote/branch] //将当前分支设置为跟踪[remote/branch]的分支

git branch -vv (注意是两个v不是w) //将所有的本地分支列出来并且包含更多的信息。

git push orgin --delete [branch] //删除远程分支。这个命令做的只是从服务器上移除这个指针。 Git 服务器通常会保留数据一段时间直到垃圾回收运行,所以如果不小心删除掉了,通常是很容易恢复的

git merge的完全使用手册:https://www.jianshu.com/p/58a166f24c81


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了。这么做的好处是提交历史没有分支(某个提交有两个父提交的情况不存在了)。具体代码如下:

  1. git checkout test //切换到test分支
  2. git rebase master //将当前分支(test)变基到基底(master)分支,此处可能有冲突,需要手动解决,解决之后用git add标记冲突已解决(add后不要commit),然后用git rebase --continue命令 继续rebase。这一步的具体实现过程如下:将test 分支里的每个提交取消掉,并且把他们临时保存为补丁(patch,这些补丁放在.git/rebase 目录中),然后把test分支更新到最新的maser分支(这里用master的最新的commitID也可以,而且更显示了这个操作的本质),最后把保存的这些补丁应用到 test分支上。
  3. git checkout master //切换到master分支
  4. 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 的分叉历史。
Figure 58. John 的分叉历史

John 有一个引用指向 Jessica 推送上去的改动,但是他必须将它们合并入自己的工作中之后才能被允许推送。

$ git merge origin/master
Merge made by recursive.
 TODO |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

合并进行地很顺利 - John 的提交历史现在看起来像这样:

合并了 `origin/master` 之后 John 的仓库。
Figure 59. 合并了 origin/master 之后 John 的仓库

现在,John 可以测试代码,确保它依然正常工作,然后他可以把合并的新工作推送到服务器上:

$ git push origin master
...
To john@githost:simplegit.git
   fbff5bc..72bbc59  master -> master

最终,John 的提交历史看起来像这样:

推送到 `origin` 服务器后 John 的历史。
Figure 60. 推送到 origin 服务器后 John 的历史

在此期间,Jessica 在一个特性分支上工作。 她创建了一个称作 issue54 的特性分支并且在那个分支上做了三次提交。 她还没有抓取 John 的改动,所以她的提交历史看起来像这样:

Jessica 的特性分支。
Figure 61. Jessica 的特性分支

Jessica 想要与 John 同步,所以她进行了抓取操作:

# Jessica's Machine
$ git fetch origin
...
From jessica@githost:simplegit
   fbff5bc..72bbc59  master     -> origin/master

那会同时拉取 John 推送的工作。 Jessica 的历史现在看起来像这样:

抓取 John 的改动后 Jessica 的历史。
Figure 62. 抓取 John 的改动后 Jessica 的历史

Jessica 认为她的特性分支已经准备好了,但是她想要知道必须合并什么进入她的工作才能推送。 她运行 git log 来找出:

$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith <jsmith@example.com>
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 的历史看起来像这样:

合并了 John 的改动后 Jessica 的历史。
Figure 63. 合并了 John 的改动后 Jessica 的历史

现在 origin/master 是可以从 Jessica 的 master 分支到达的,所以她应该可以成功地推送(假设同一时间 John 并没有再次推送):

$ git push origin master
...
To jessica@githost:simplegit.git
   72bbc59..8059c15  master -> master

每一个开发者都提交了几次并成功地合并了其他人的工作。

推送所有的改动回服务器后 Jessica 的历史。
Figure 64. 推送所有的改动回服务器后 Jessica 的历史

这是一个最简单的工作流程。 你通常在一个特性分支工作一会儿,当它准备好整合时合并回你的 master 分支。 当想要共享工作时,将其合并回你自己的 master 分支,如果有改动的话然后抓取并合并 origin/master,最终推送到服务器上的 master 分支。 通常顺序像这样:

一个简单的多人 Git 工作流程的通常事件顺序。
Figure 65. 一个简单的多人 Git 工作流程的通常事件顺序

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:大项目工作流

就是分支更多了,有四个长期分支:masternext,用于新工作的 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分支即可。
  • 上面的命令有以下几个同义语句:
  1. git rebase -i f42c5 //ruby_client分支上执行
  2. git rebase -i master //ruby_client分支上执行
  3. 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 <commit / tag /branch/ head / head^ ....>

分支名可以代替这个分支的最后一个提交对象(的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 <schacon@gmail.com>)
Reflog message: commit: fixed refs handling, added gc auto, updated
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Jan 2 18:32:33 2009 -0800

    fixed refs handling, added gc auto, updated tests

commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Reflog: master@{1} (Scott Chacon <schacon@gmail.com>)
Reflog message: merge phedders/rdocs: Merge made by recursive.
Author: Scott Chacon <schacon@gmail.com>
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.

Example history for range selection.
Figure 137. 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/mastergit 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 和 ackgit 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 <gitster@pobox.com>
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 <gitster@pobox.com>
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 总结

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客
应支付0元
点击重新获取
扫码支付

支付成功即可阅读