自用的Git使用手册。不适合Git入门,可以用于Git速查和复习。持续更新 …
在项目目录下:
git init
提示Initialized empty Git repository in xxx
即表示成功创建一个Git repository(仓库)。初始化repository时不会自动track(跟踪)项目中的已有文件。
此时,打开项目目录,可以发现一个.git
目录,记录git初始化信息和之后的版本信息,修改它可能导致版本丢失。
使用git add
命令将文件添加到暂存区中。实际使用中,可能添加单个、多个、某些、所有文件,可通过参数来实现。
添加指定文件到暂存区。git add
后面跟文件名,支持单个或多个文件,适用于你修改了较少文件且记得每个文件名的时候,注意,即使文件被删除了,你也要add被删除的文件,这样暂存区才会记录它给删除了。
git add file1.txt file2.txt
可以多次使用git add
来在不同时间添加不同文件,如上面可写成
git add file1.txt
git add file2.txt
添加所有文件。有时候,我们不清楚哪些被修改过了,那直接添加所有的文件最简单不过。
git add -A # all,添加全部被修改过的文件
它等价于git add .
和git add *
。
添加某些文件。如果你想添加某些文件,一一列出文件名比较繁琐,可以使用通配符。
git add *.txt #添加所有后缀为.txt的文件
添加某个文件夹下的文件。如果add后的文件名是一个文件夹(/
可有可无),它会自动添加该文件夹下的所有文件。
git add dirname
更新文件。“更新”一词是git对已经跟踪的文件而言,所有它不会添加untracked(未跟踪文件)的文件,即你新增的文件。
git add -u
值得一提,你可能错误添加了某个你不想添加的文件,可以使用git reset HEAD filename
达到加其从暂存区删除的效果。
使用git commit
命令将暂存区的文件提交到版本库,同时生成一个版本。
-m
参数用于指定commit信息,是必须的,而且不能为空,如果漏了,git会自动打开文本编辑器要求输入。如果实在不想指定commit信息,可以使用git commit --allow-empty -m ""
参数。这???还是-m "."
省事。
经常地,会提交暂存区添加的文件到版本库:
git commit -m "message"
可以直接把工作区的文件直接提交到版本库:
git commit <filename> -m "message"
该命令不会连同暂存区的文件一起commit。
如果想把上版本之后的修改全部commit,可以使用:
git commit -am "add all files from working tree"
省去git add .
。当然上面用git commit . -m "message"
也是可以。
一般来说,commit提交都会生成新的版本。如果某次提交后,发现add
时漏了一个文件,或者文件有小差错,此时不想新增一个版本,而是对最近commit的版本的补充修改,可以使用--amend
参数。设置了该参数的commit不会生成新的版本,而是更新修改上次commit的内容(包括文件和信息)。
如果上次commit时疏漏了file.txt
,或者该文件有瑕疵,可以如下更新该文件:
git add file.txt
git commit --amend --no-edit
这里--no-edit
指明不修改commit信息。或者可以直接使用一条命令:
git commit file.txt --amend --no-edit
也可以修改commit信息,如:
git add file.txt
git commit --amend -m "message"
如果发现上次commit的时候信息写错了,想修改,可以在暂存区为空的情况下执行:
git commit --amend -m "message"
有很多git命令可以查看git版本控制相关的信息,这些信息对git的使用很有帮助。
git status
可查看文件状态信息, 该命令默认会显示所有未跟踪、在缓存区 、修改过的文件:
上面file1.txt
出现在两处是因为在执行git add
之后又修改了。
git status filename
可以接查看指定文件的状态信息。
git log
可查看版本日志。会显示版本的哈希码、提交人、提交message、提交时间等信息。按q
可退出日志。
使用git log --graph
会以树状图的形式展示分支,更加清晰明了。
git diff
可以查看文件差异,跟踪我们修改了文件什么内容。
默认会列出所有暂存区/工作区有区别的文件,命令后面接文件名git diff filename
可以查看特定文件。
可以使用参数查看别处的文件差异对比,如下:
git diff --cached #查看仓库与暂存区的差异
git diff HEAD #查看仓库与工作区的差异
git diff [<commit-id>] [<commit-id>] #比较两个commit之间的差异
git diff branch1 branch2 #比较两个分支之间的差异
git reflog
可以查看所有分支操作记录,包括被删除的版本或分支的记录。
一个项目在在上个版本能正常运行,修改文件之后,发现运行不了,又找不到是修改了哪里导致的问题,这时候,庆幸用了git版本记录,回退到上个版本就行了。
git reset
命令可以进行版本穿梭。
在此之前,了解HEAD
指针,默认地,它总指向最新的一次commit,可以理解为它指向当前版本。版本切换的本质就是改变HEAD
指向的commit,从单词reset也是reset HEAD之意。
除了第一个commit,每一个commit都有且仅有一个前继commit,也就是有明确的上一版本,并且每个版本也会记录其前继版本。HEAD^
表示前一版本,HEAD^^
或HEAD~2
表示上上版本,HEAD^^^
或HEAD~3
表示往前3个版本,以此类推。
每个commit都有对应的commit id,用于表示每次commit,它是一串长长的十六进制哈希码。
现在,可以开始随意的版本切换了。
最常用的,就是回退到上一个版本:
git reset HEAD^
如果不想数往上多少个版本,可以通过指定commit id进行切换,commit id很长,但一般指定4位就够了。如:
git reset 8c6a #commit id全长:8c6a47caf4de1d9e227a89e6421db4fd03a87ba9
因为git没有记录下继版本,所以,当你回退到某个版本之后,使用git log
发现只有在当前commit之前的commit,而之后的commit丢失了。是的,回退之后,不能再前进。虽然丢失了,但并没有被删除,通过git reflog
可以检出HEAD的指向的历史commit,包括丢失了的,从中获取commit id,通过git reset commit-id
就可以跳到该版本了。
如果细心点,会发现使用git reset
的过程中,工作目录中的文件并没有变化。这涉及到一个问题,版本切换的时候,想把工作目录的文件与目标版本一致吗?暂存区要恢复到什么状态?还是只想更改HEAD指针而已?reset的mode参数指明了这个。reset有三种模式,默认--mixed
, 还有--hard
,soft
。
git reset --soft HEAD^
。只把HEAD指针指向上一版本,暂存区和工作区文件都不变化。git reset --hard HEAD^
。把HEAD指针指向上版本,工作目录和文件和指定版本一致,暂存区清空。git reset --mixed HEAD^
。把HEAD指针指向上版本,工作目录不变,暂存区清空,即所有在目标commit之后add到暂存区的文件都会变成unstaged。如果指向恢复某个文件,可以使用git checkout
命令。
撤销工作区对文件的修改:
git checkout <filename>
git的修改是指相对于上次add到暂存区而言的。如果你最近commit之后没有add该文件,它会恢复到HEAD指向的commit,如果被add过,则恢复到与暂存区一致。
指定了commit之后,工作区和暂存区的文件都会和commit保持一致。
git checkout HEAD <filename>
分支(branch)是Git中极为重要的概念,分支上的工作不会影响主线,保证了多人开发的并发性。git默认分支称为主分支(master)。想查看分区,可以使用命令
git branch
如果还没有创建任何分支,只会看到master主分支。
git branch
接分支名可以创建一个新分支。
git branch branchA # 创建分支branchA
可以用git branch
查看分支,此时*
在master前面,说明当前所在的分支为master,也就是上面的命令不会自动转到新建分支。
很自然的,你想要到新分支下工作。可以使用git checkout
切换分支。
git checkout branchA
再使用git brach
看看*
号的位置。
经常都是想到新分支上工作才创建新分支的,可以使用参数-b
在创建后自动切换到该分支上。
git checkout -b branchB
我一开始以为是git branch -b branchname。。。好吧,这条命令不是"创建并切换", 它意思是"切换到该分支,如果分支不存在,新建后再切换",然后我试着用一个已存在分支切换,无法执行,提示“该branch已存在”。意思还是“创建并切换”,那我就搞不懂这个事为什么不归git branch管了。
当某个分区支的工作完成后,需要把它和master分支合并起来,使用git merge
可以合并分支。
git merge branchB
合并后HEAD仍然指向当前分支。所以,一般会先切换到master,在master上与分支合并,然后删除分支。
如果两个分支存在祖先关系,即一个分支是另一个分支的祖先,Git会采用Fast-forward
合并方式。
假设在master上创建并切换到一个新分支newbranch,没有切换到newbranch分支,而是在master上做了文件修改并commit后,发现本来是想在newbranch做这些修改的,那这时候,能直接在master上使用
git merge newbranch
合并吗?结果newbranch并没有被合并,而是提示已是最新。想要合并二者,必须切换到newbranch上执行git merge master
。从使用者意图来说,我们希望Fast-forward总是把旧的分支合并到新分支上。但是,从设计上来说,允许前者以为着在当前分支上修改了另一个分支,这是不合适的,显然这里采用了后者,保证merge命令只是修改HEAD指向的分支指向。
如果两个分支分别commit过,git会合并到一个新commit,当前分支指向新commit,而被合并的分支不变。此时采用three-way-merge
的方式,即根据公共祖先、当前分支、被合并分支比较合并。合并原理可以看怎么理解Git里的 “three-way merge” ? - Lazykid的回答 - 知乎。
然而,合并过程往往不是一帆风顺。如果两个分支对同一个文件的同一地方做了不同的修改,git无法明确该采用哪个文件。这种情况也就是出现了冲突。
出现冲突的时候,Git不会生成合并commit,而是进入冲突待解决状态,可以使用git status
查看。冲突文件被标记为冲突的,产生冲突的地方使用<<<<< ===== >>>>>
标记出来。此时,你必须解决冲突或者中断本次合并。放弃合并使用git merge --abort
。
冲突需要手动解决。也就是修改文件上标记的地方。修改完之后,使用git add
命令告诉git你已经解决了冲突。然后使用git commit
完成合并。
假设某次尝试合并的过程中file.txt
产生冲突。手动修改file.txt之后,使用:
git add file.txt
git commit -m "merge message"
即可完成合并。
合并之后,分支的任务一般就结束了,应该删除该分支。即使开发新功能,也是新建一个branch。下面命令删除分支newbranch
。
git branch -d newbranch
前提是HEAD不指向newbranch。
以Github为例。
初次使用github时,为了本机的推送都可以不用账号密码通过验证,需要创建SSH密钥对。
在电脑cmd上创建密钥对:
ssh-keygen -t rsa -C "[email protected]"
登录github,在个人设置里面,把公钥码复制到上面。
github 创建仓库很简单,按照指引就行。
创建github仓库之后,本地仓库需要与远程仓库建立连接。
git remote add origin <your repository url>
建立连接之后就可推送到远程库了。
git push -u origin master
查看远程库,确实推上去了。
因为在首次推送之后,本地与远程仓库已经建立连接,所以可以简化命令,直接使用:
git push origin master
把远程库内容克隆到本地:
git clone <your repository url>
使用
git fetch origin <branch name>
可以从远程库拉取分支。
拉取的分支以origin/xxx
存在,需要用git branch -a
才能看到。拉取分支并不会对本地工作产生影响,相当于新建了一个分支。
如果想把远程分支跟本地分支合并,可以使用git merge origin/master
。
或者直接使用
git pull origin <branch name>
可以实现上面拉取并合并的效果。当然,合并可能会产生在本地分支合并过程中的冲突,用同样的方式解决即可。
多人协作时,如果推送失败,需要先拉取,再推送。
git stash #保存本地修改
git stash list #查看stash列表
git stash apply #应用最新的stash
git stash apply stash@{1} #应用特定stash
git stash drop #删除最新stash
git stash drop stash@{1} #删除特定stash
git stash pop #应用并删除最新stash
在其它分支上完成的工作可能对目前工作有帮助,如bug消除,在别的分支消除bug并commit之后,本分支无须重复一次工作,使用:
git cherry-pick commit-id
在本分支上复刻其它分支的commit。
git stash #保存本地修改
git stash list #查看stash列表
git stash apply #应用最新的stash
git stash apply stash@{1} #应用特定stash
git stash drop #删除最新stash
git stash drop stash@{1} #删除特定stash
git stash pop #应用并删除最新stash
在其它分支上完成的工作可能对目前工作有帮助,如bug消除,在别的分支消除bug并commit之后,本分支无须重复一次工作,使用:
git cherry-pick commit-id
在本分支上复刻其它分支的commit。