《Pro Git》笔记
+ git的每一次提取(clone, fetch, pull)都是对仓库的完整备份,其关心的是文件整体的变化而非内容的差异,它将变化的文件做快照(每次运行git add命令时,都会将变化文件的快照保存在staged区域中。),然后保存在内部的微型文件系统中。每次提交(commit)更新时, git会会纵览一遍staged区域中所有文件的快照(其实staged区域中有工作目录中已跟踪所有文件的快照,只不过没变化的文件是历史快照而已。),然后保存一个指向这次快照的索引。
+ git的操作基本上都是只“添加”数据到仓库中,即使执行git rm --cached命令,也只是将相应文件的状态设置为“未追踪”而不是删除。只要添加到仓库中的数据,不管当前目录是否还有它,都可以恢复。
+ git管理的文件(已追踪)有四种状态和三个位置:未修改,已修改,已暂存,已提交;前两个状态的文件位于工作目录中,已暂存的文件位于staged区域中,已提交的文件位于git 仓库中。
- staged区域中存的是已修改文件的快照,但可能不是最终版本:在提交前,我们可以多次通过git add命令将同一个文件添加到staged区域中,提交时使用的最后一次添加的版本。
+ git reset HEAD filename #将staged区域中filename的快照丢弃,目录中filename文件内容不变但是状态为已修改。
- git checkout -- filename #丢弃对当前目录中filename文件的修改,将其恢复为已保存的快照内容(注意,快照的内容可能是staged区域中未提交更新(恢复后的文件状态为已暂存),也可能是仓库中的(恢复后的文件状态为未修改)。)。注意,这种方法也适合恢复以前提交的但是当前在工作目录中已经删除的文件。
- 如果想保存当前对工作目录中文件的修改,但又想退回到以前的某个版本继续开发,则可以使用stashing或分支机制。如将当前目录中的内容提交到一个临时分支中。然后checkout历史版本继续工作。
+ git内部设有已跟踪文件清单、未跟踪文件清单、以及用户设置的忽略跟踪文件清单。可以使用gir rm命令将某个文件从已跟踪文件清单和当前目录中删除(仓库中的历史快照文件并不会删除)。使用git rm时支持glob模式,对于没有指定路径的单个文件名匹配所有目录下的该模式文件。
+ git rm filename #将当前工作目录中的filename文件删除,同时git停止对该文件的跟踪。如果staged区域中有filename未提交的快照,则删除失败,这时可以使用git rm -f filename命令强制删除(注意,有可能造成数据丢失)。
- git rm --cached filename #停止对filename的跟踪,但不删除工作目录中的文件。如果staged区域中有filename未提交的快照,则会将该快照标记为删除状态,在下一次提交时将停止跟踪。
+ git status #查看已暂存和未暂存的更新,只列出相关的文件。
+ git diff #将当前工作目录中的已跟踪文件与其已保存的快照文件(可能是staged区域中,也可能是仓库中的)进行差异比较,diff的第一个参数为快照文件。
- git diff --cached或--staged #将staged区域中的文件与已commit的repo中的文件进行比较,diff的第一个参数为repo中的文件。
+ 一般在项目开始的时候就在工作目录及各个子目录中设立.gitignore文件。如果某个文件已被git跟踪,然后再将其添加到.gitignore文件,这时git不会自动忽略该文件,除非用户执行:git rm --cached file-name命令。也就是说对于已跟踪的文件,.gitignore是不起作用的。
- .gitignore使用的bash的glob文件模式,如果匹配目录中所有文件,则需在目录名后加正斜线(这是因为git的递归特性,如果不加正斜线,git将不能区分该文件是目录文件还是普通文件。);如果不忽略指定模式文件或目录,需在模式前加叹号(这适合先定义一个宽松的忽略条件,然后再指定不忽略的文件)。
- git一般递归地处理目录名下的文件,例如 git rm --cached Document/\*.a会递归地删除Document目录下的所有以.a结尾的文件,而不仅仅是Document目录下的该类型文件。但是如果用git rm --cached Document/*.a 则是后一种情况(因为git看不到这个*号,它看到的只是被shell扩展后的文件名列表)。
+ git的配置文件位置可以用--system/--global/--file指定,配置命令为git config ,配置项目的引用格式为 段名.变量名。
+工作目录中的文件只有两种状态:已跟踪和未跟踪。初次clone某个库时,所得到的文件的状态为已跟踪但未修改。
+ git add是一个多用途命令,其功能取决于所作用的文件当前状态:它可以开始跟踪新文件,将修改的文件添加到暂存区,提交合并时冲突的文件。git add name,name可以是一个目录也可以是一个pattern,git将递归地添加目录中的所有文件。git add -u pattern,只添加已经追踪的文件更新。
+ 提交时记录的是放在暂存区域的快照,任何还未暂存的仍然保持已修改状态,可以在下次提交时纳入版本管理。每一次运行提交操作,都是对你整个项目作一次快照,以后可以回到这个状态,或者进行比较。
+ git commit -a -m messages #跳过暂存区,将当前目录中所有已跟踪的文件的快照添加到仓库中(如果暂存区中有未提交的快照则会丢弃)。
+ Git 并不跟踪文件移动操作。如果在 Git 仓库中利用mv命令重命名了某个文件,仓库中存储的元数据并不会体现出这是一次改名操作。所以,git提过了git mv oldname newname命令,它等效为三条命令:mv oldname newname; git rm oldname; git add newname.
+ git log命令用于查看历史的提交记录,默认不用任何参数的话,git log 会按提交时间列出所有的更新。git log 有许多选项可以帮助你搜寻感兴趣的提交,这些选项可以联合使用
选项 说明
-p 按补丁格式显示每个更新之间的差异。
--stat 显示每次更新的文件修改统计信息,包括更新的各个文件的详细信息和最后的统计信息。
--shortstat 只显示 --stat 中最后修改的文件数目,添加、删除的行数。
--name-only 仅在提交信息后显示已修改的文件清单。
--name-status 除了显示已修改的文件清单外,还在文件名前添加一个字符如N,M,D表示新增、修改、删除的文件清单。
--abbrev-commit 仅显示 SHA-1 的前几个字符,而非所有的 40 个字符。
--relative-date 使用较短的相对时间显示(比如,“2 weeks ago”)。
--graph 显示 ASCII 图形表示的分支合并历史。
--pretty 使用其他格式显示历史提交信息。可用的选项包括 oneline,short,full,fuller 和 format(后跟指定格式)。
format的参数:
选项 说明
%H 提交对象(commit)的完整哈希字串
%h 提交对象的简短哈希字串
%T 树对象(tree)的完整哈希字串
%t 树对象的简短哈希字串
%P 父对象(parent)的完整哈希字串
%p 父对象的简短哈希字串
%an 作者(author)的名字
%ae 作者的电子邮件地址
%ad 作者修订日期(可以用 -date= 选项定制格式)
%ar 作者修订日期,按多久以前的方式显示
%cn 提交者(committer)的名字
%ce 提交者的电子邮件地址
%cd 提交日期
%cr 提交日期,按多久以前的方式显示
%s 提交说明
下面这些选项可以对上面的输出结果进行筛选,
-(n) 仅显示最近的 n 条提交
--since, --after 仅显示指定时间之后的提交,如:--since=2.weeks
--until, --before 仅显示指定时间之前的提交
--author 仅显示指定作者相关的提交。
--committer 仅显示指定提交者相关的提交。
如果只关心某些文件或者目录的历史提交,可以在 git log 选项的最后指定它们的路径。因为是放在最后位置上的选项,所以用两个短划线 (--)隔开之前的选项和后面限定的路径名(使用bash的glob通配符)。如:$ git log --pretty="%h:%s" --author=gitster --since="2008-10-01" \
--before="2008-11-01" --no-merges -- t/
远程仓库的管理:
+ 管理远程仓库的工作,包括添加远程库,移除废弃的远程库,管理各远程库分支,定义是否跟踪这些分支,等等。
- 本地仓库的建立有两种方法,1.使用git clone url [dirname]命令从远程的git代码仓库服务器中克隆到本地的dirname目录中。2. 在本地要建立仓库的目录中运行git init命令。对于前者,git会自动添加名为origin的远程仓库,同时建立本地master分支来跟踪origin的master分支。对本地仓库,最多只能clone自一个远程仓库。
- 要查看本地仓库与哪些远程仓库相关联,可以用 git remote 命令,它会列出每个远程库的简短名字。也可以加上 -v 选项(为 --verbose 的简写,取首字母),显示详细信息。
- 远程仓库的信息记录在本地./git/config文件的remote section中。因此,可以直接查看或修改该文件。
- 要添加一个新的远程仓库,可以指定一个简单的名字,以便将来引用,运行 git remote add [shortname] [url],现在可以用字串 shortname 指代对应的仓库地址了。比如说,要抓取所有 Paul 有的,但本地仓库没有的信息,可以运行 git fetch shortname。fetch到的objects将保存到本地的remote/shortname分支中,例如shortname中的master分支,在本地可以通过shortname/master分支访问。注意,shortname/master分支只用于保存远程仓库中的信息,本地用户不能直接修改其中的内容。但是可以通过将其中的某一个分支合并到本地分支,然后在本地分支中修改并提交后再提交到远程仓库中。或者只是checkout其中的某个分支一探究竟。
- 现在,Paul 的主干分支(master)已经完全可以在本地访问了(但是还没有被跟踪),对应的名字是 pb/master,你可以将它合并到自己的某个分支,或者切换到这个分支,看看有些什么有趣的更新。远程仓库的分支在本地仓库中以 remote-name/branch-name的形式访问。
- git branch -r 查看本地仓库中跟踪的远程分支。
- git fetch [remote-name] 此命令会到远程仓库中拉取所有你本地仓库中还没有的数据。运行完成后,你就可以在本地访问该远程仓库中的所有分支,将其中某个分支合并到本地,或者只是取出某个分支。。有一点很重要,需要记住,fetch 命令只是将远端的数据拉到本地仓库,并不自动合并到当前工作分支,只有当你确实准备好了,才能手工合并。
- 如果本地仓库创建了某个分支用于跟踪某个远端仓库的分支(可以使用git remote show命令查看远程分支的配置、跟踪情况,可以使用git checkout -b branchname remote-branch-name创建跟踪远程分支的本地分支。),如果当前处于该分支中,则可以使用 git pull 命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分支。默认情况下 git clone 命令本质上就是自动创建了本地的 master 分支用于跟踪远程仓库中的 master 分支(假设远程仓库确实有master 分支)。
- git push [remote-name] [branch-name]可以将本地仓库branch-name数据推送到远程仓库,只有当对远程服务器仓库上有写权限,或者同一时刻没有其他人在推数据,这条命令才会如期完成任务。如果在你推数据前,已经有其他人推送了若干更新,那你的推送操作就会被驳回。你必须先把他们的更新抓取到本地,合并到自己的项目中,然后才可以再次推送。对clone到本地的仓库,git push, git fetch和git pull不用指定任何参数。
- git remote show [remote-name] 查看某个远程仓库的详细信息,git会通过连接远程仓库服务器获得相关信息。
- git remote rename 命令修改某个远程仓库的名称,相关文件也会自动修改。注意,对远程仓库的重命名,也会使对应的分支名称发生变化,原来的 pb/master 分支现在成了 paul/master。
- git remote rm 命令,移除本地与之关联的远端仓库,但是并不删除相关的文件。
git remote命令是管理远程仓库的重要命令,下面显示的内容都可以通过该命令修改。
1 [geekard@geekard note]$ git remote show web 2 Enter passphrase for key '/home/geekard/.ssh/id_rsa': 3 4 remote web 5 6 Fetch URL: git@github.com:geekard/Notes.git 7 Push URL: git@github.com:geekard/Notes.git 8 HEAD branch: master #git追踪的远程仓库的默认分支由相应仓库的HEAD(refs/remote/web/HEAD)指向,这里为web的master分支。 9 Remote branch: 10 master tracked 11 Local branch configured for 'git pull': 12 github merges with remote master #本地处于github分支时,如果执行git pull命令,则远程web的master分支与本地的github分支会自动合并 13 Local ref configured for 'git push': 14 master pushes to master (local out of date) 15 #git pull , git fetch , git push都可以指定refspec参数,用于指出本地分支和远程分支的关系。 16 17 [geekard@geekard note]$ git branch -r #列出本地仓库跟踪的远程仓库的分支 18 hardisk/HEAD -> hardisk/master 19 hardisk/master 20 web/master 21 [geekard@geekard note]$ 22 23 [geekard@geekard note]$ cat .git/config 24 [core] 25 repositoryformatversion = 0 26 filemode = true 27 bare = false 28 logallrefupdates = true 29 [remote "hardisk"] #远程仓库名 30 url = /media/backup/Sync/Notes.git/ #仓库的URL,同时蕴含了协议类型 31 fetch = +refs/heads/*:refs/remotes/hardisk/* #git fetch hardisk时默认使用的refspec:远程refs:本地refs 32 [remote "web"] 33 url = git@github.com:geekard/Notes.git #使用的ssh协议 34 fetch = +refs/heads/*:refs/remotes/web/* #使用git fetch web时默认使用的refspec. git push的refspec默认。 35 [branch "github"] #本地分支名 36 remote = web #该分支跟踪的远程仓库名称 37 merge = refs/heads/master #当在本分支上执行git pull时,使用的远程refspec,即将远程refspec的内容与本分支合并. 38 [branch "master"] 39 remote = hardisk 40 merge = refs/heads/master
标签管理
+ Git 也可以对某一时间点上的版本打上标签,也就是为某次commit的快照打上标签。
- git tag #列出当前分支中现有标签。可以用特定的搜索模式列出符合条件的标签,如$ git tag -l 'v1.4.2.*'
- Git 使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用 GNU Privacy Guard (GPG) 来签署或验证。
- git tag -a v1.4 -m 'my version 1.4' #为当前commit打上附注标签。
- 用 git show 命令查看相应标签的版本信息,并连同显示打标签时的提交对象: git show v1.4
- 如果你有自己的私钥,还可以用 GPG 来签署标签,只需要把之前的 -a 改为 -s (译注:取 Signed 的首字母)即可:
git tag -s v1.5 -m 'my signed 1.5 tag'
- 轻量级标签实际上就是一个保存着对应提交对象的校验和信息的文件。要创建这样的标签,一个 -a,-s 或 -m 选项都不用,直接给出标签名字即可: git tag v1.4-lw
- 可以使用 git tag -v [tag-name] (译注:取 verify 的首字母)的方式验证已经签署的标签。此命令会调用 GPG 来验证签名,所以你需要有签署者的公钥,存放在 keyring 中,才能验证。
- 可以在后期对早先的某次提交加注标签,只要在打标签的时候跟上对应提交对象的校验和(或前几位字符,可以通过git log --pretty=online获得)即可:
$ git tag -a v1.2 9fceb02
- 默认情况下,git push 并不会把标签传送到远端服务器上,只有通过显式命令才能分享标签到远端仓库。其命令格式如同推送分支,运行
git push origin [tagname] ; 如果要一次推送所有(本地新增的)标签上去,可以使用 --tags 选项: git push origin --tags
撤销更改
+ git commit --amend修改最后一次提交。此命令将使用当前的暂存区域快照提交,这个快照可以与上次commit时的不同,但是git会以当前的为准(上次提交时的暂存区快照会和当前暂存区合并,但不会替换同名文件的快照。例如上次提交时新增了一个文件,而后用git rm删除了该文件,这时运行git commit --amend时,该文件就不会被添加进仓库。)。如果刚才提交完没有再将任何文件放到暂存区,直接运行此命令的话,相当于有机会重新编辑提交说明,而所提交的文件快照和之前的一样。启动文本编辑器后,会看到上次提交时的说明,编辑它确认没问题后保存退出,就会使用新的提交说明覆盖刚才失误的提交。如果刚才提交时忘了暂存某些修改,可以先补上暂存操作,然后再运行 --amend 提交.
注意:不要将已提交到远程仓库中的commit执行amend操作,因为别人在获得了的amend前的commit时再获得amend后的版本时会出错。
+git reset HEAD file-name取消当前放到暂存区的文件,而工作区中的该文件内容不受影响,但状态为已更改。如果暂存区中的文件file-name在工作区中已经更改(例如对添加到暂存区但未提交的文件再次修改),则取消失败,除非指定-f参数。
+git checkout -- file-name 取消对文件file-name的修改(先从暂存区中恢复文件,如果暂存区中没有则从已提交的仓库中恢复。),工作目录中的文件将会被恢复。
+git checkout HEAD file-name 取消对文件file-name的修改,但是是从已提交的仓库中恢复的。因此,会改变暂存区和当前工作目录中该文件的内容。
+ 任何已经提交到仓库中的文件都可以恢复,所以,你可能失去的数据,仅限于没有提交过的(如工作目录中修改的文件,暂存区中未提交的快照),对 Git 来说它们就像从未存在过一样。
分支管理
+ 在 Git 中提交时,会保存一个提交(commit)对象,它包含一个指向暂存内容快照的指针,作者和相关附属信息,以及一定数量(也可能没有)指向该提交对象直接祖先的指针。
- 当使用 git commit 新建一个提交对象前,Git 会先计算每一个子目录(本例中就是项目根目录)的校验和,然后在 Git 仓库中将这些目录保存为树(tree)对象。之后 Git 创建的提交对象,除了包含相关提交信息以外,还包含着指向这个树对象(项目根目录)的指针,如此它就可以在将来需要的时候,重现此次快照的内容了。
- Git 仓库中有多种对象:表示文件快照内容的 blob 对象;一个记录着目录树内容及其中各个文件对应 blob 对象索引的 tree 对象;以及一个包含指向 tree 对象的索引和其他提交信息元数据的 commit 对象。修改后再次提交,那么这次的提交对象会包含一个指向上次提交对象的指针(parent)。
- Git 中的分支,其实本质上仅仅是个指向 commit 对象的可变指针。Git会使用 master 作为分支的默认名字。在若干次提交后,你其实已经有了一个指向最后一次提交对象的 master 分支,它在每次提交的时候都会自动向前移动。
- 要创建一个分支仅需要创建一个分支指针即可,可以使用 git branch name,这会在当前 commit 对象上新建一个分支指针.
- Git 是如何知道你当前在哪个分支上工作的呢?其实答案也很简单,它保存着一个名为 HEAD 的特别指针。在 Git 中,它是一个指向你正在工作中的本地分支的指针。运行 git branch 命令,仅仅是建立了一个新的分支,但不会自动切换到这个分支中去。
- 要切换到其他分支,可以执行 git checkout branch-name 命令,这条命令做了两件事:它把 HEAD 指针指向branch-name分支,并把工作目录中的文件换成了branch-name分支所指向的快照内容。因此,在使用git checkout 命令前,最好查看当前分支的工作目录的所有修改是否已经提交。
- git checkout branch-name:检出branch-name上最后一次提交的快照到当前工作目录中,如果当前工作目录中有未提交的修改与即将检出的快照中同名文件冲突(如,快照中的文件与当前的修改有冲突或比当前新),则git会阻止本次切换,直到将当前工作目录中的未保存的冲突文件提交或丢弃。但是如果未提交的修改不与即将检出的版本冲突(如新建了一个文件,处于untracked 状态)或修改的文件在检出的快照中不存在,则切换成功,而且这个修改会保留在切换到的分支中。如果想避免当前目录中文件遗留在检索出的快照目录,可以使用git checkout -b new-branch-name branch; 对于已提交的修改,即使两者不同也不会出错,因为git 会使用快照中的版本替换当前目录中的版本同时当前版本因已提交到仓库中所以也不会丢失
— 例如,在master分支中新添加一个文件dumy.c,然后编辑它,但不stage,即文件处于untrack状态。这时切换到dev分支后,该文件就会出现在工作目录中
- 这些改变(对于已提交的更改)分别孤立在不同的分支里:我们可以在不同分支里反复切换,并在时机成熟时把它们合并到一起。同时,因为每次提交时都记录了祖先信息(译注:即 parent 对象),所以以后要合并分支时,寻找恰当的合并基础(译注:即共同祖先)的工作其实已经完成了一大半,实现起来非常容易。
- git创建一个分支就是创建一个指向特定快照的指针,其实就是在.git/refs/heads目录下创建一个保存指向快照提交对象的SHA-1字符串的文件。因此,git创建和销毁分支非常迅速。
- git checkout -b branch-name 新建并切换到branch-name分支中(注意:该分支以当前分支的快照为基础,目录通常应该保持干净。如果不想将目录中的修改提交,可以创建一个临时分支,然后将修改提交到该临时分支中,然后再运行该命令)。该命令其实是下面两条命令的简缩版:git branch branch-name; git checkout branch-name.
- git checkout -b new-branch-name branch 创建branch分支的跟踪分支new-branch-name,然后切换到该分支中。在该跟踪分支中使用git pull或git push命令时,将自动从branch分支拉取合并或向branch推送该分支的提交。所以该命令的branch参数一般为fetch到本地的远程分支。
- Git checkout命令会把工作目录的内容恢复为检出某分支时它所指向的那个commit 的快照。它会自动添加、删除和修改文件以确保目录的内容和你上次提交时完全一样。
- git merge branch-name会将branch-name分支中的提交的修改合并到当前分支中。如果顺着一个分支(A)走下去可以到达另一个分支(B),那么 Git 在合并两者时(处在分支A中,运行git merge B),只会简单地把指针(A)前移,因为没有什么分歧需要解决,所以这个过程叫做快进(Fast forward)。
- git branch -d branch-name 删除已经合并的分支branch-name,如果该分支还没有被合并过,需要用-D选项来删除它。
- 对于非快进式分支合并操作,git不得不进行一些处理:git会用两个分支的末端和它们的共同祖先进行一次简单的三方合并计算,并将合并的结果作为一个新的快照,并自动创建一个指向它的commit对象,该对象称为合并提交,因为它的直接祖先不止一个。
- 有时修改了两个待合并分支中同一文件的同一部分,git就无法自动地合并它们,这时它会报告merge conflict。git做了合并,但是没有提交,它会暂停下来等你解决它指出来的冲突。可用git status查看冲突的文件.任何合并后包含未解决的冲突的文件都以unmerged状态列出,同时文件中加入了冲突解决标记。你需要打开并修改这些冲突文件,然后删除冲突解决标记,并用git add命令将这些文件再次提交(提交状态显示为resolved)。然后用git status确认,最后用git commit提交。
- git branch 命令不带参数时,列出仓库中所有的本地分支。git branch -v列出所有本地仓库中分支的同时,显示最后一次提交的信息。git branch -r 显示所有的远程分支。git branch --merged或--no-merged,从分支清单中只列出已经合并的分支或未合并的分支,对于已经合并的分支可以使用git branch -d branch-name删除。未合并的分支可以使用-D选项强制删除。
远程分支及其跟踪分支
- 远程分支(remote branch)是对远程仓库状态的索引。它们是一些无法移动的本地分支;只有在进行 Git 的网络活动时才会更新。远程分支就像是书签,提醒着你上次连接远程仓库时上面各分支的位置。git 用 远程仓库名/分支名 这样的形式表示远程分支,因此本地可以使用与远程同名的分支而不会混淆。
- 远程仓库的分支保存在./.git/refs/remotes/仓库名 目录中
- git clone URL命令会自动地将远程仓库命名为origin,并在本地建立一个origin/master的指向远程仓库的master的指针,但是你无法在本地对该分支进行更新。git会自动在本地建立一个属于你自己的本地master分支,该分支用于跟踪origin/master分支的更新。你可以用git pull命令从origin/master拉取数据并自动合并到master分支中,也可以在master分支中修改数据,然后使用git push命令更新到远程的origing/master分支中。
- 如果你在本地的master分支中做了修改,与此同时别人向origin/master分支推送了内容,那么你们的提交历史将朝不同方向发展。但是只要你不和服务器通信,你的origin/master指针不会移动。你可以先使用git fetch origin将别人的更新拉取到本地的oringin/mater分支中,同时origin/master指针也将移动到远程仓库中的最新位置。
- git push origin master 将本地的master分支推送到origin的master分支中。这里的master是refs/heads/master:refs/heads/master的简写。git push origin master:YY, 将本地的master分支推送到远程仓库的YY分支中。
- git fetch origin 从origin仓库中抓去所有的更新到本地。如果远程有新的分支YY,则git会在本地建立origin/YY分支来指向它。但是你无法在本地编辑origin/YY分支,因为它是无法移动的用于指向服务器中YY分支的指针。你可以使用git merge origin/master将远程分支合并到当前分支中。也可以使用git checkout -b new-branch origin/YY命令创建一个远程分支的跟踪分支(tracking branch),该分支以origin/YY为基础分化而来。可以在该分支中编辑文件然后使用git push命令推送到服务器中,使用git pull从远程分支中抓取并合并更新。
- git push origin :branch-name 在远程仓库中删除名为branch-name的分支。这里最后一个参数完整的形式是 本地分支:远程分支,但现在本地分支为空,所以意味着删除仓库中的branch-name分支。
衍合(rebase)
- 把一个分支中的数据整合到另一个分支中有两种办法:merge和rebase。
- rebase命令是把在一个分支中提交的改变在另一个分支中重放一遍。衍合能产生一个更为干净的提交历史记录。
- 将分支A中的修改衍合到分支B中:1.先切换到分支A中,git checkout A; 2.在分支A中执行:git rebase master;这时git会查找A,B的共同祖先,然后将A分支中的每次提交差异在分支B中提交(每一次重演都使用分支A的提交信息如日期与提交说明),最终分支A的指针指向A,B合并后的结果对象。3.切换到B分支中,执行一次快速合并:git checkout B; git merger A。
- 无论是衍合还是三方合并,最后生成的快照对象指向的是相同的内容(但是它们的SHA-1值不同),只是提交的历史记录不同罢了。衍合是将发生的改变在另一个分支中重演,而合并是将两个分支的最后提交对象合并在一起。
- git rebase --onto master server client;命令的含义是:检出client分支,找出client分支与server分支共同祖先后的变化,然后把他们在分支master上重演一遍。注意,rebase后client指向最新的生成对象,而master和server指针不变。这时可以快进master指针了:git checkout master; git merge client.
-git rebase [主分支] [特性分支] ; 找出特性分支和主分支的共同祖先,然后将特性分支的修改在主分支上重演。注意,rebase后特性分支的指针指向最新的rebase对象,而主分支指针不变,但是可以用用快进的方式指向最新的合并对象。使用这条命令不需要事先切换到特性分支中。
- 衍合的风险:永远不要衍合哪些已经推送到公共仓库的更新。因为衍合实际上是删除一些commit,然后创造一些类似(提交日期和提交说明与特性分支相同)但新的commit。应该将衍合作为一种在推送之前清理历史记录的手段,而且仅仅衍合那些永远不会公开的commit。
服务器上的Git
- 服务器上的git仓库一般是一个纯仓库(bare repository) — 一个没有当前工作目录的仓库。可以使用命令$ git clone --bare my_project my_project.git来从已有的仓库建立一个纯仓库,大体上相当于:$ cp -Rf my_project/.git my_project.git;但是在配置文件中有几个小改变;不过从效果角度讲,克隆的内容是一样的。
- Git可以使用四种协议来传输数据:本地传输,SSH协议,Git协议,和HTTP协议。Git协议和HTTP协议通常是只读的,因为他们没有授权机制。SSH协议是唯一可以用来对git仓库进行读写的协议。路径中不指明协议时,git默认使用ssh协议。
- 架设git服务器最复杂的方面在于账户管理,如需要仓库对一些用户只读,而对另外一些用户可都写。
- 简单的情况下可以使用服务器操作系统提供的本地文件访问许可机制即可,但是如果需要团队中的每个人都对仓库有写权限,但是又不想给每个人在服务器上建立一个账户,那么SSH机制就是较好的选择。
- 让其他人能访问git服务器的方法有很多。第一个办法是给每个人建立一个账户,直截了当但过于繁琐。反复的运行 adduser 并且给所有人设定临时密码可不是好玩的。第二个办法是在主机上建立一个 git 账户,让每个需要写权限的人发送一个 SSH 公钥,然后将其加入 git 账户的 ~/.ssh/authorized_keys 文件。这样一来,所有人都将通过 git账户访问主机。这丝毫不会影响提交的数据——访问主机用的身份不会影响commit的记录。第三个办法是让 SSH 服务器通过某个 LDAP 服务,或者其他已经设定好的集中授权机制,来进行授权。只要每个人都能获得主机的 shell 访问权,任何可用的 SSH 授权机制都能达到相同效果。
- 简单的匿名读取权限可以使用一个静态的WEB服务来实现,把它的根目录设为git仓库所在的目录。然后开启post钩子, mv hooks/post-update.sample hooks/post-update;chmod a+x hooks/post-update。post-update 挂钩的作用是当通过 SSH 向服务器推送时,Git 将运行这个钩子文件中的命令来更新 HTTP 获取所需的文件。
- 架设一个简单的网页界面使其更加可视化。为此,Git 自带了一个叫做 GitWeb 的 CGI 脚本。安装过程如下:
$ git clone git://git.kernel.org/pub/scm/git/git.git
$ cd git/
$ make GITWEB_PROJECTROOT="/opt/git" prefix=/usr gitweb/gitweb.cgi
$ sudo cp -Rf gitweb /var/www/
上面的GITWEB-PROJECTROOT指定了已经存在的git仓库的根目录。然后make命令会将这个仓库中的内容和cgi脚本放到gitweb目录下。
接着将gitweb目录放到HTTP服务器读取的更目录下。
- 权限管理器 Gitosis ;Gitosis 简单的说就是一套用来管理authorized_keys 文件和实现简单连接限制的脚本。最有意思的是,该软件用来添加用户和设定权限的界面不是网页,而是一个特殊的 Git 仓库。你只需要设定好某个项目;然后推送,Gitosis 就会随之改变服务器设定,酷吧?
分布式GIT
+ 在 Git 网络中,每个开发者同时扮演着节点和集线器的角色,这就是说,每一个开发者都可以将自己的代码贡献到另外一个开发者的仓库中,或者建立自己的公共仓库,让其他开发者基于自己的工作开始,为自己的仓库贡献代码。于是,Git 的分布式协作便可以衍生出种种不同的工作流程。
+ 集中式工作流:集中式工作流程使用的都是单点协作模型。一个存放代码仓库的中心服务器,可以接受所有开发者提交的代码。所有的开发者都是普通的节点,作为中心集线器的消费者,平时的工作就是和中心仓库同步数据。如果两个开发者从中心仓库克隆代码下来,同时作了一些修订,那么只有第一个开发者可以顺利地把数据推送到共享服务器。第二个开发者在提交他的修订之前,必须先下载合并服务器上的数据,解决冲突之后才能推送数据到共享服务器上。这么做是告诉第二个开发者:你所作的修订无法通过快近(fast-forward)来合并,你必须先拉取最新数据下来,手工解决冲突合并后,才能继续推送新的提交。这么做是因为,第二个开发者提交到服务器上的remote/branch与服务器上现有的branch没有父子关系,服务器端的git不知如何合并它们,因此拒绝开发者的提交。
+ 集成管理员工作流:这种情形通常都会有个代表着官方发布的项目仓库(blessed repository),开发者们由此仓库克隆出一个自己的公
共仓库(developer public),然后将自己的提交推送上去,请求官方仓库的维护者拉取更新合并到主项目。维护者在自己的本地也有个克隆仓库(integration manager),他可以将你的公共仓库作为远程仓库添加进来,经过测试无误后合并到主干分支,然后再推送到官
方仓库。在 GitHub 网站上使用得最多的就是这种工作流。
+ 司令官与副官工作流:这其实是上一种工作流的变体。一般超大型的项目才会用到这样的工作方式,像是拥有数百协作开发者的 Linux 内核项目就是如此。各个集成管理员分别负责集成项目中的特定部分,所以称为副官(lieutenant)。而所有这些集成管理员头上还有一位负责统筹的总集成管理员,称为司令官(dictator)。司令官维护的仓库用于提供所有协作者拉取最新集成的项目代码。