git config --list
检查配置信息,列出Git当时能找到的所有配置
git init
创建一个名为.git的子目录
git clone
克隆现有的仓库,
git status
查看文件处于什么状态
$ git status On branch master //当前分支 Your branch is up-to-date with 'origin/master'. //分支同远程服务器上对应的分支 nothing to commit, working directory clean
status命令输出的格式比较详细,但是用语有些繁琐.如果你使用 git status -s
命令或 git status --short
命令,你将得到一种格式更为紧凑的输出
$ git status -s M README MM Rakefile A lib/git.rb M lib/simplegit.rb ?? LICENSE.txt
git add
将文件纳入暂存区 git add 命令使用文件或目录的路径作为参数;如果参数是目录的路径,该命令将递归地跟踪该目录下的所有文件
如果在文件add之后,又修改了该文件,查看文件状态会发现文件出现在暂存区和非暂存区,Git 只暂存了你运行 git add 命令时的版本,所以运行了 git add 之后又作了修订的文件,需要重新运行 git add 把最新版本重新暂存起来
*.[oa] * ~
第一行告诉 Git 忽略所有以 .o 或 .a 结尾的文件 第二行告诉 Git 忽略所有名字以波浪符(~)结尾的文件 文件 .gitignore 的格式规范如下:
所有空行或者以 # 开头的行都会被 Git 忽略。
可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
匹配模式可以以(/)开头防止递归。
匹配模式可以以(/)结尾指定目录。
要忽略指定模式以外的文件或目录,可以在模式前加上叹号(!)取反。 例子:
# 忽略所有的 .a 文件 *.a # 但跟踪所有的 lib.a,即便你在前面忽略了 .a 文件 !lib.a # 只忽略当前目录下的 TODO 文件,而不忽略 subdir/TODO /TODO # 忽略任何目录下名为 build 的文件夹 build/ # 忽略 doc/notes.txt,但不忽略 doc/server/arch.txt doc/*.txt # 忽略 doc/ 目录及其所有子目录下的 .pdf 文件 doc/**/*.pdf
git diff
git commit
git commit -m 将提交信息与命令放在同一行
git commit -a Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤
要从 Git 中移除某个文件,就必须要从已跟踪文件清单中移除(确切地说,是从暂存区域移除),然后提交。 可以用 git rm
命令完成此项工作,并连带从工作目录中删除指定的文件,这样以后就不会出现在未跟踪文件清单中了
另外一种情况是,我们想把文件从 Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。 换句话说,你想让文件保留在磁盘,但是并不想让 Git 继续跟踪。
为达到这一目的,使用 --cached
选项
git rm --cached README
个人感觉后者可能使用会比较多
当你不适用编辑器的git工具时,你应该会用到重命名
要在 Git 中对文件改名,可以这么做
git mv file_from file_to
运行 git mv
就相当于运行了下面三条命令
$ mv README.md README $ git rm README.md $ git add README
如此分开操作,Git 也会意识到这是一次重命名,所以不管何种方式结果都一样。 两者唯一的区别在于,git mv
是一条命令而非三条命令,直接使用 git mv
方便得多。 不过在使用其他工具重命名文件时,记得在提交前 git rm
删除旧文件名,再 git add
添加新文件名
当你在此项目中运行 git log
命令时,可以看到下面的输出
$ git log commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott ChaconDate: Mon Mar 17 21:52:11 2008 -0700 changed the version number commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Author: Scott Chacon Date: Sat Mar 15 16:40:33 2008 -0700 removed unnecessary test commit a11bef06a3f659402fe7563abf99ad00de2209e6 Author: Scott Chacon Date: Sat Mar 15 10:31:28 2008 -0700 first commit
不传入任何参数的默认情况下,git log
会按时间先后顺序列出所有的提交,最近的更新排在最上面。 正如你所看到的,这个命令会列出每个提交的 SHA-1 校验和、作者的名字和电子邮件地址、提交时间以及提交说明
git log
有许多选项可以帮助你搜寻你所要找的提交
-p
或 --patch
,它会显示每次提交所引入的差异(按 补丁 的格式输出)。 你也可以限制显示的日志条目数量,例如使用 -2
选项来只显示最近的两次提交。该选项除了显示基本信息之外,还附带了每次提交的变化
--stat
选项,看到每次提交的简略统计信
另一个非常有用的选项是 --pretty
。 这个选项可以使用不同于默认格式的方式展示提交历史。 这个选项有一些内建的子选项供你使用。 比如 oneline
会将每个提交放在一行显示,在浏览大量的提交时非常有用。 另外还有 short
,full
和 fuller
,format
选项,它们展示信息的格式基本一致,但是详尽程度不一
$ git log --pretty=oneline ca82a6dff817ec66f44342007202690a93763949 changed the version number 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test a11bef06a3f659402fe7563abf99ad00de2209e6 first commit
format
比较多,可以查看官网
这是在使用 Git 的过程中,会因为操作失误而导致之前的工作丢失的少有的几个地方之一
有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 --amend
选项的提交命令来重新提交
git commit --amend
这个命令会将暂存区中的文件提交。 如果自上次提交以来你还未做任何修改(例如,在上次提交后马上执行了此命令), 那么快照会保持不变,而你所修改的只是提交信息。
例如,你提交后发现忘记了暂存某些需要的修改,可以像下面这样操作:
$ git commit -m 'initial commit' $ git add forgotten_file $ git commit --amend
最终你只会有一个提交——第二次提交将代替第一次提交的结果。
当你在修补最后的提交时,与其说是修复旧提交,倒不如说是完全用一个 新的提交 替换旧的提交, 理解这一点非常重要。从效果上来说,就像是旧有的提交从未存在过一样,它并不会出现在仓库的历史中。
修补提交最明显的价值是可以稍微改进你最后的提交,而不会让“啊,忘了添加一个文件”或者 “小修补,修正笔误”这种提交信息弄乱你的仓库历史。
例如,你已经修改了两个文件并且想要将它们作为两次独立的修改提交, 但是却意外地输入 git add *
暂存了它们两个。如何只取消暂存两个中的一个呢
git reset HEAD
git reset HEAD CONTRIBUTING.md
git reset
确实是个危险的命令,如果加上了--hard
选项则更是如此。 然而在上述场景中,工作目录中的文件尚未修改,因此相对安全一些。
将它还原成上次提交时的样子(或者刚克隆完的样子,或者刚把它放入工作目录时的样子)
git checkout --
git checkout -- CONTRIBUTING.md
请务必记得
git checkout --
是一个危险的命令。 你对那个文件在本地的任何修改都会消失——Git 会用最近提交的版本覆盖掉它。 除非你确实清楚不想要对那个文件的本地修改了,否则请不要使用这个命令。
如果你仍然想保留对那个文件做出的修改,但是现在仍然需要撤消,我们将会在 Git 分支 介绍保存进度与分支,这通常是更好的做法。
记住,在 Git 中任何 已提交 的东西几乎总是可以恢复的。 甚至那些被删除的分支中的提交或使用
--amend
选项覆盖的提交也可以恢复 (阅读 数据恢复 了解数据恢复)。 然而,任何你未提交的东西丢失后很可能再也找不到了
git remote
它会列出你指定的每一个远程服务器的简写
origin
——这是 Git 给你克隆的仓库服务器的默认名字
-v
,会显示需要读写远程仓库使用的 Git 保存的简写与其对应的 URL
git remote -v
git remote add
加一个新的远程 Git 仓库,同时指定一个方便使用的简写
git remote add <简写名称> 远程仓库地址 git remote add pb https://github.com/paulboone/ticgit
现在你可以在命令行中使用字符串 pb
来代替整个 URL
如果你想拉取 Paul 的仓库中有但你没有的信息,可以运行 git fetch pb
git fetch
这个命令会访问远程仓库,从中拉取所有你还没有的数据
必须注意 git fetch
命令只会将数据下载到你的本地仓库——它并不会自动合并或修改你当前的工作。 当准备好时你必须手动将其合并入你的工作
如果你的当前分支设置了跟踪远程分支(阅读下一节和 Git 分支 了解更多信息), 那么可以用 git pull
命令来自动抓取后合并该远程分支到当前分支。
默认情况下,git clone
命令会自动设置本地 master 分支跟踪克隆的远程仓库的 master
分支(或其它名字的默认分支)。 运行 git pull
通常会从最初克隆的服务器上抓取数据并自动尝试合并到当前所在的分支。
git push
当你想要将 master
分支推送到 origin
服务器时(再次说明,克隆时通常会自动帮你设置好那两个名字), 那么运行这个命令就可以将你所做的备份到服务器
git push origin master
只有当你有所克隆服务器的写入权限,并且之前没有人推送过时,这条命令才能生效。 当你和其他人在同一时间克隆,他们先推送到上游然后你再推送到上游,你的推送就会毫无疑问地被拒绝。 你必须先抓取他们的工作并将其合并进你的工作后才能推送
git remote show
查看某一个远程仓库的更多信息
git remote show origin
git remote rename
来修改一个远程仓库的简写名
想要将 pb
重命名为 paul
,可以用 git remote rename
这样做
git remote rename pb paul
值得注意的是这同样也会修改你所有远程跟踪的分支名字。 那些过去引用 pb/master
的现在会引用 paul/master
如果因为一些原因想要移除一个远程仓库——你已经从服务器上搬走了或不再想使用某一个特定的镜像了, 又或者某一个贡献者不再贡献了
git remote remove
或 git remote rm
git remote remove origin
一旦你使用这种方式删除了一个远程仓库,那么所有和这个远程仓库相关的远程跟踪分支以及配置信息也会一起被删除
git tag
(可带上可选的 -l
选项 --list
)
在 Git 中列出已有的标签
这个命令以字母顺序列出标签,但是它们显示的顺序并不重要
也可以按照特定的模式查找标签
例如,Git 自身的源代码仓库包含标签的数量超过 500 个。 如果只对 1.8.5 系列感兴趣,可以运行
git tag -l "v1.8.5*"
按照通配符列出标签需要
-l
或--list
选项如果你只想要完整的标签列表,那么运行
git tag
就会默认假定你想要一个列表,它会直接给你列出来, 此时的-l
或--list
是可选的。然而,如果你提供了一个匹配标签名的通配模式,那么
-l
或--list
就是强制使用的。
Git 支持两种标签:轻量标签(lightweight)与附注标签(annotated)
轻量标签很像一个不会改变的分支——它只是某个特定提交的引用。
而附注标签是存储在 Git 数据库中的一个完整对象, 它们是可以被校验的,其中包含打标签者的名字、电子邮件地址、日期时间, 此外还有一个标签信息,并且可以使用 GNU Privacy Guard (GPG)签名并验证。 通常会建议创建附注标签,这样你可以拥有以上所有信息。
tag
命令时指定 -a
选项
git tag -a v1.4 -m "my version 1.4"
-m
选项指定了一条将会存储在标签中的信息。 如果没有为附注标签指定一条信息,Git 会启动编辑器要求你输入信息
通过使用 git show
命令可以看到标签信息和与之对应的提交信息
轻量标签本质上是将提交校验和存储到一个文件中——没有保存任何其他信息
创建轻量标签,不需要使用 -a
、-s
或 -m
选项,只需要提供标签名字
git tag v1.4-lw
这时,如果在标签上运行 git show
,你不会看到额外的标签信息。 命令只会显示出提交信息
你也可以对过去的提交打标签。 假设提交历史是这样的
$ git log --pretty=oneline 15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch 'experiment' a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support 0d52aaab4479697da7686c15f77a3d64d9165190 one more thing 6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch 'experiment' 0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function 4682c3261057305bdd616e23b64b0857d832627b added a todo file 166ae0c4d3f420721acbb115cc33848dfcc2121a started write support 9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile 964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo 8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme
现在,假设在 v1.2 时你忘记给项目打标签,也就是在 “updated rakefile” 提交。 你可以在之后补上标签。 要在那个提交上打标签,你需要在命令的末尾指定提交的校验和(或部分校验和)
git tag -a v1.2 9fceb02
默认情况下,git push
命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到共享服务器上
git push origin
git push origin v1.5
如果想要一次性推送很多标签,也可以使用带有 --tags
选项的 git push
命令。 这将会把所有不在远程仓库服务器上的标签全部传送到那里
git push origin --tags
git push
推送两种标签使用
git push
推送标签并不会区分轻量标签和附注标签, 没有简单的选项能够让你只选择推送一种标签--tags
要删除掉你本地仓库上的标签,可以使用命令 git tag -d
git tag -d v1.4-lw
注意上述命令并不会从任何远程仓库中移除这个标签,你必须用 git push
来更新你的远程仓库
第一种变体是 git push
git push origin :refs/tags/v1.4-lw
第二种更直观的删除远程标签的方式是
git push origin --delete
想查看某个标签所指向的文件版本,可以使用 git checkout
命令, 虽然这会使你的仓库处于“分离头指针(detached HEAD)”的状态——这个状态有些不好的副作用
建议去看原文地址,这个地方需要特别注意
Git - 打标签
Git 并不会在你输入部分命令时自动推断出你想要的命令。 如果不想每次都输入完整的 Git 命令,可以通过 git config
文件来轻松地为每一个命令设置一个别名。
$ git config --global alias.co checkout $ git config --global alias.br branch $ git config --global alias.ci commit $ git config --global alias.st status
这意味着,当要输入 git commit
时,只需要输入 git ci
$ git config --global alias.unstage 'reset HEAD --' 等价 $ git unstage fileA $ git reset HEAD -- fileA
可以看出,Git 只是简单地将别名替换为对应的命令。 然而,你可能想要执行外部命令,而不是一个 Git 子命令。 如果是那样的话,可以在命令前面加入 !
符号。 如果你自己要写一些与 Git 仓库协作的工具的话,那会很有用
git branch
git branch testing
git branch
命令仅仅 创建 一个新分支,并不会自动切换到新分支中去
可以简单地使用 git log
命令查看各个分支当前所指的对象。 提供这一功能的参数是 --decorate
git log --oneline --decorate
那么,Git 又是怎么知道当前在哪一个分支上呢? 也很简单,它有一个名为 HEAD
的特殊指针
在 Git 中,它是一个指针,指向当前所在的本地分支
要切换到一个已存在的分支,你需要使用 git checkout
命令
get checkout
这样 HEAD
就指向指定的分支了
分支切换会改变你工作目录中的文件
在切换分支时,一定要注意你工作目录里的文件会被改变。 如果是切换到一个较旧的分支,你的工作目录会恢复到该分支最后一次提交时的样子。 如果 Git 不能干净利落地完成这个任务,它将禁止切换分支。
通常我们会在创建一个新分支后立即切换过去,这可以用
git checkout -b
一条命令搞定。
git merge <分支名>
比如我要切换回master分支,并合并a分支
$ git checkout master $ git merge a
当你试图合并两个分支时, 如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候, 只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”
git branch -d <分支名>
有时候合并操作不会如此顺利。 如果你在两个不同的分支中,对同一个文件的同一个部分进行了不同的修改,Git 就没法干净的合并它们
如果你对问题的修改和有关 `分支的修改都涉及到同一个文件的同一处,在合并它们的时候就会产生合并冲突
$ 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 会在有冲突的文件中加入标准的冲突解决标记,这样你可以打开这些包含冲突的文件然后手动解决冲突。 出现冲突的文件会包含一些特殊区段,看起来像下面这个样子
<<<<<<< HEAD:index.html======= >>>>>>> iss53:index.html
这表示 HEAD
所指示的版本(也就是你的 master
分支所在的位置,因为你在运行 merge 命令的时候已经检出到了这个分支)在这个区段的上半部分(=======
的上半部分),而 iss53
分支所指示的版本在 =======
的下半部分。 为了解决冲突,你必须选择使用由 =======
分割的两部分中的一个,或者你也可以自行合并这些内容
冲突解决方案仅保留了其中一个分支的修改,并且 <<<<<<<
, =======
, 和 >>>>>>>
这些行被完全删除了。 在你解决了所有文件里的冲突之后,对每个文件使用 git add
命令来将其标记为冲突已解决。 一旦暂存这些原本有冲突的文件,Git 就会将它们标记为冲突已解决
如果你想使用图形化工具来解决冲突,你可以运行 git mergetool
,该命令会为你启动一个合适的可视化合并工具,并带领你一步一步解决这些冲突
git branch
命令不只是可以创建与删除分支。 如果不加任何参数运行它,会得到当前所有分支的一个列表
$ git branch iss53 * master testing
如果需要查看每一个分支的最后一次提交,可以运行 git branch -v
命令
--merged
与 --no-merged
这两个有用的选项可以过滤这个列表中已经合并或尚未合并到当前分支的分支。 如果要查看哪些分支已经合并到当前分支可以运行 git branch --merged
查看所有包含未合并工作的分支,可以运行 git branch --no-merged
这里显示了其他分支。 因为它包含了还未合并的工作,尝试使用 git branch -d
命令删除它时会失败
如果真的想要删除分支并丢掉那些工作,如同帮助信息里所指出的,可以使用 -D
选项强制删除它。
上面描述的选项
--merged
和--no-merged
会在没有给定提交或分支名作为参数时, 分别列出已合并或未合并到 当前 分支的分支。你总是可以提供一个附加的参数来查看其它分支的合并状态而不必检出它们。 例如,尚未合并到master
分支的有哪些?$ git checkout testing $ git branch --no-merged master topicA featureB
git ls-remote
来显式地获得远程引用的完整列表
或者通过 git remote show
获得远程分支的更多信息
git push
也可以运行 git push origin serverfix:serverfix
它会做同样的事——也就是说“推送本地的 serverfix
分支,将其作为远程仓库的 serverfix
分支” 可以通过这种格式来推送本地分支到一个命名不相同的远程分支
下一次其他协作者从服务器上抓取数据时,他们会在本地生成一个远程分支
origin/serverfix
,指向服务器的serverfix
分支的引用要特别注意的一点是当抓取到新的远程跟踪分支时,本地不会自动生成一份可编辑的副本(拷贝)。 换一句话说,这种情况下,不会有一个新的
serverfix
分支——只有一个不可以修改的origin/serverfix
指针可以运行
git merge origin/serverfix
将这些工作合并到当前所在的分支。 如果想要在自己的serverfix
分支上工作,可以将其建立在远程跟踪分支之上$ git checkout -b serverfix origin/serverfix这会给你一个用于工作的本地分支,并且起点位于
origin/serverfix
。
从一个远程跟踪分支检出一个本地分支会自动创建所谓的“跟踪分支”(它跟踪的分支叫做“上游分支”)。 跟踪分支是与远程分支有直接关系的本地分支。 如果在一个跟踪分支上输入 git pull
,Git 能自动地识别去哪个服务器上抓取、合并到哪个分支
当克隆一个仓库时,它通常会自动地创建一个跟踪 origin/master
的 master
分支。 然而,如果你愿意的话可以设置其他的跟踪分支,或是一个在其他远程仓库上的跟踪分支,又或者不跟踪 master
分支。 最简单的实例就是像之前看到的那样,运行 git checkout -b
。 这是一个十分常用的操作所以 Git 提供了 --track
快捷方式:
$ git checkout --track origin/serverfix
如果想要将本地分支与远程分支设置为不同的名字,你可以轻松地使用上一个命令增加一个不同名字的本地分支:
$ git checkout -b sf origin/serverfix
设置已有的本地分支跟踪一个刚刚拉取下来的远程分支,或者想要修改正在跟踪的上游分支, 你可以在任意时间使用 -u
或 --set-upstream-to
选项运行 git branch
来显式地设置
$ git branch -u origin/serverfix
上游快捷方式当设置好跟踪分支后,可以通过简写
@{upstream}
或@{u}
来引用它的上游分支。 所以在master
分支时并且它正在跟踪origin/master
时,如果愿意的话可以使用git merge @{u}
来取代git merge origin/master
。
git branch -vv
这会将所有的本地分支列出来并且包含更多的信息,如每一个分支正在跟踪哪个远程分支与本地分支是否是领先、落后或是都有
当 git fetch
命令从服务器上抓取本地没有的数据时,它并不会修改工作目录中的内容。 它只会获取数据然后让你自己合并。 然而,有一个命令叫作 git pull
在大多数情况下它的含义是一个 git fetch
紧接着一个 git merge
命令。 如果有一个像之前章节中演示的设置好的跟踪分支,不管它是显式地设置还是通过 clone
或 checkout
命令为你创建的,git pull
都会查找当前分支所跟踪的服务器与分支, 从服务器上抓取数据然后尝试合并入那个远程分支。
由于 git pull
的魔法经常令人困惑所以通常单独显式地使用 fetch
与 merge
命令会更好一些
可以运行带有 --delete
选项的 git push
命令来删除一个远程分支。
如果想要从服务器上删除 serverfix
分支,运行下面的命令
$ git push origin --delete serverfix To https://github.com/schacon/simplegit - [deleted] serverfix
本上这个命令做的只是从服务器上移除这个指针。 Git 服务器通常会保留数据一段时间直到垃圾回收运行,所以如果不小心删除掉了,通常是很容易恢复的
你可以提取在 新分支节点中引入的补丁和修改,然后在
要被合并的分支上 的基础上应用一次。 在 Git 中,这种操作就叫做 变基(rebase)。 你可以使用 rebase
命令将提交到某一分支上的所有修改都移至另一分支上,就好像“重新播放”一样
你可以检出 B 分支,然后将它变基到 master
(A) 分支上
$ git checkout A $ git rebase master
它的原理是首先找到这两个分支(即当前分支 B、变基操作的目标基底分支 master
) 的最近共同祖先 节点,然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件, 然后将当前分支指向目标基底 节点, 最后以此将之前另存为临时文件的修改依序应用
一般我们这样做的目的是为了确保在向远程分支推送时能保持提交历史的整洁——例如向某个其他人维护的项目贡献代码时。 在这种情况下,你首先在自己的分支里进行开发,当开发完成时你需要先将你的代码变基到 origin/master
上,然后再向主项目提交修改。 这样的话,该项目的维护者就不再需要进行整合工作,只需要快进合并便可
请注意,无论是通过变基,还是通过三方合并,整合的最终结果所指向的快照始终是一样的,只不过提交历史不同罢了。 变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在一起