Git命令和配置技巧
此文主要介绍一切开发中常用的git命令和一些配置技巧(诸如git别名配置,log打印技巧,版本回退以及分支管理等)。
1.简介
Git与svn相比而言,Git的好处自然不用多说,Git完全分布式文件管理系统,加上其简单速度,可以高效管理类似 Linux 内核一样的超大规模项目。完全分布式的系统,让你可以在公交车上,火车上,家中,甚至在厕所都可以敲代码。何时何地你都可以敲代码,甚至不需要网络。好不好使,开不开心?换是SVN,SVN服务器挂了,全部人停止敲代码,停下来八卦去吧。
Git 和其他版本控制系统的主要差别在于,Git 只关心文件数据的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异。这类系统(CVS,Subversion,Perforce,Bazaar 等等)每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容。如同下图所示:
但是,Git 并不保存这些前后变化的差异数据。实际上,Git 更像是把变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这次快 照的索引。为提高性能,若文件没有变化,Git 不会再次保存,而只对上次保存的快照作一连接。工作方式类似下图:
对比可以发现,Git高效也在情理之中。
现在再简单介绍一下Git管理下文件的三种状态。对于任何一个文件,只要在Git管理下,那么该文件只有三种状态:已修改(modified),已暂存(staged)和已提交 (committed)。
已修改(modified): 文件被修改,但是还没有提交保存(也就是没有使用git add,此时使用git status显示为红色)。
已暂存(staged): 已修改的文件放入下次要提交的清单中(使用了git add后的状态,此时使用git status显示为绿色)。
已提交(committed): 该文件已经被安全地保存在本地数据库中(使用了git commit后,此时使用git status已经不存在该文件的任何信息)。
具体可以参考下面两幅图来理解。
2.配置用户信息
user和email,--global参数全局配置,当然你也可以不加此参数,不同的项目用不同的用户名和邮箱。
git config --global user.name Super git config --global user.email 1342449****@163.com
3.配置全局别名
此配置在开发中相当重要,尤其是对于使用Terminal,习惯使用命令行的朋友,由于git不支持tab自动补全,每次想要看下工作目录 状态都要git status,相当耗时。除非你能确定你敲两个字母比六个字母用时少。
git config --global alias.st "status -s" git config --global alias.ci "commit -m" git config --global alias.aci "commit -a -m" // 跳过使用暂缓区,直接将git add和git commit合并为一条命令 git config --global alias.lg "log --color --pretty=format:'%Cred%h - %Cgreen%an %C(yellow)| %ad | %Creset%s' --graph" (自定义log)
第一条:git status是开发中使用最多最频繁的,至于-s 是简洁输入(Give the output in the short-format)
第二条:此条也使用频繁,但是我在开发中直接使用第三条跳过。
第三条:配置git aci 因为这样直接跳过使用暂存区域,对于已经跟踪的文件,我不要再此次使用git add加入暂缓区,然后再git commit提交到本地数据库,为了方便省事,直接将两条命令合并为一条,使用git aci "提交说明" 即可。省不省事,用下自然知道。
第四条:这里是我自定义的log信息,当然,一会看完本文你也可以自己格式化成自己喜欢的log格式。可以先看下效果,下图所示:
以后直接使用"git lg"即可,非常方便,显示简洁明了。
4.分支管理
git checkout -b dev origin/dev // 拉取远程开发分支到本地并且切换到开发分支 git push dev origin/dev git branch new // 创建新分支 git push origin new // 将new分支推送到远程(git push [remote-name] [branch-name]) git push origin :new // 删除远程分支,注意冒号位置 git brach -d new // 删除本地分支,如果有没有merge的信息,确保该分支的确不用merge,直接使用-D强行删除 git merge dev // 将dev分支合并到当前分支 git checkout master // 切换到主分支 // 其他不再一一列出
关于实际开发中,独立开发的话其实两个分支完全够用,一个主分支,一个开发分支。多人的话就按功能模块和人员来具体新建分支即可。由于这边由我一人负责整个项目,那么分支的话,我就建立了一个develop分支。发布版本时放到主分支处理即可。平时都在develop分支开发,如果此时发现线上版本有bug,那么只需要切换到master分支,修改bug,然后封版即可。处理好后继续回到开发分支开发就可以了。当然最好还是把刚才修改bug的代码merge到开发分支上。
还有一点使用技巧,也就是git stash的灵活使用。当我们正在当前分支编写代码,突然某人中断你的思路,提出某功能需修改且比较紧急,更烦人的是当前的代码已经开始编写,而且动用了好多文件,重要的是不想中途进行git commit提交。此时git stash就发挥作用了。
git stash // 暂时隐藏,之后就可以正常切换分支了,当前的修改内容只是保存并且隐藏起来 git stash pop // 回复到之前的工作状态,默认pop的是最近的一条,这样就可以愉快的继续编写当时被中断的代码了
git stash可以执行多次,我们可以使用git stash list命令来查看清单。当然还有好多命令,可以使用"git stash --help"来查看。
// git stash --help usage: git stash list [<options>] or: git stash show [<stash>] or: git stash drop [-q|--quiet] [<stash>] or: git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>] or: git stash branch <branchname> [<stash>] or: git stash [save [--patch] [-k|--[no-]keep-index] [-q|--quiet] [-u|--include-untracked] [-a|--all] [<message>]] or: git stash clear
其实关于git stash还有一个使用技巧,那就是如果当前改了众多文件,突然又不想改了,想恢复到原来的样子。那么可能你会使用"git checkout ."命令来取消所有的更改,但是有种情况并不好使,那就是你除了修改文件,还添加了一些文件或则拖入了一些文件到工程目录下,此时使用"git checkout ."然后再使用"git status"查看的时候,会发现工作区多出了尚未识别的文件(处于等待add的状态)。在这个时候,使用git stash是再好不过了,因为这样将所有git控制下的文件,包括之前的目录,全部还原到之前的状态(也就是说git stash把新增的文件也暂时隐藏起来)。如果想彻底删掉,那就再把所有stash的列表清空吧,直接"git stash list"看下,然后"git stash clear"。
还有一点,关于在Git服务器上删除分支,本地使用"git branch -a"依旧可以看到被删除问题。好比你的代码托管到了开源中国(OSChina),你通过网站,在线删除分支,就会出现这种问题,稍微有点强迫症的自然受不了。此时可以使用 "git fetch -p"使fetch之后删除没有与远程分支对应的本地分支。 当然也可以通过查看远程分支。使用命令"git remote show origin"显示如下:
YJTSuper:yjtim super$ git remote show origin * remote origin Fetch URL: [email protected]:lingsui/yjtim.git Push URL: [email protected]:lingsui/yjtim.git HEAD branch: master Remote branches: dev tracked im tracked master tracked proV2.3.0 tracked refs/remotes/origin/test stale (use 'git remote prune' to remove) Local branches configured for 'git pull': master merges with remote master show merges with remote master Local refs configured for 'git push': dev pushes to dev (up to date) im pushes to im (fast-forwardable) master pushes to master (up to date) proV2.3.0 pushes to proV2.3.0 (up to date)
我们可以看到分支origin/test 已经过期(stale),可以使用命令"git remote prune origin"同样可以处理。
5.远程地址切换
如果想要切换远程地址,千万不要重新再初始化一个git代码仓库,使用git add重新添加。这样的好处只有一点,所有历史提交信息全部清空,不再保留,"git clone"时文件变小,理所当然,历史记录全部清空了麽。如果确定不保留,也建议这样做,关键时,谁写的代码更改了哪些东西很重要呀,还是选择保留吧。使用下面的方法,一行代码搞定,并且保留了所有的commit记录。
git remote -v // 查看远程地址 git remote set-url origin https://git.oschina.net/HaiShengHuo/xxx.git // 更换远程地址, 新建一个项目不添加任何文件 在本地直接push即可
6.查看某次提交修改的具体文件
方法一
git reflog
列出最新修改记录 也可以 git log --onelinegit log 00788a04 --name-status
找到对应版本号 执行
commit 00788a04f8d6dd834723c3479a0b5cdcfad8694a Author: ZhiChao <[email protected]> Date: Thu Mar 30 11:04:46 2017 +0800 最后一条消息显示来源人 M yjtim/IM/Chat/Controller/ConversationListController.m commit a4b9129f1728dc10d5f6ab8c77c1b20ef7bb3d12 Author: ZhiChao <[email protected]> Date: Wed Mar 29 17:49:44 2017 +0800 隐藏被入群弹框 M yjtim/IM/Chat/Controller/ChatDemoHelper.m commit 1910008369d4ab264e7610997135a54d27fe22c7 Author: ZhiChao <[email protected]> Date: Wed Mar 29 17:40:56 2017 +0800 99+ M yjtim/IM/EaseUI/EMUIKit/Views/conversation/toolbar/EaseImageView.m
git diff HEAD@{79} HEAD@{78}
具体某次修改的内容(具体某次内容和上次的内容进行比较)git diff a4b9129f 00788a04
当然也可以使用版本号
这样假如之前修改过具体某些内容,以后还需要修改的话,找起来真的很方便.好比某些bug好久之后才发现,又要回头去修改,而修改总要找到对应的代码进行修改吧,这样几行命令就定位到具体文件和位置了,方便多了,节省很多时间.找代码有技巧,但是"找"终究还是很浪费时间.这也告诉我们,commit 提交命令很重要,需要认真写,不可为了省事而乱写
diff --git a/yjtim/IM/Chat/Controller/ConversationListController.m b/yjtim/IM/Chat/Controller/ConversationListController.m index df0555e5..71822638 100644 --- a/yjtim/IM/Chat/Controller/ConversationListController.m +++ b/yjtim/IM/Chat/Controller/ConversationListController.m @@ -347,7 +347,7 @@ - (NSAttributedString *)conversationListViewController:(EaseConversationListView [attributedStr setAttributes:@{NSForegroundColorAttributeName : [UIColor colorWithRed:1.0 green:.0 blue:.0 alpha:0.5]} range:NSMakeRange(0, NSLocalizedString(@"group.atMe", @"[Somebody @ me]").length)]; } else { - attributedStr = [[NSMutableAttributedString alloc] initWithString:latestMessageTitle]; + attributedStr = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@:%@",[[YJTAddressList shareAddressList].userNameDict objectForKey:lastMessage.from],latestMessageTitle]]; } }
方法二
直接使用git log 6b81a31033 -p
或则 git log -p 6b81a31033
其中6b81a31033为版本哈希值
commit 6b81a31033074ef279049e9cbf5944e3158d7510 Author: ZhiChao <[email protected]> Date: Fri Apr 14 13:57:22 2017 +0800 综合评价fixbug diff --git a/yjtim/Sections/Apps/NewEvaluation/Controller/YJTEvaDetailVC.m b/yjtim/Sections/Apps/NewEvaluation/Controller/YJTEvaDetailVC.m index 10ff4b31..cef73191 100644 --- a/yjtim/Sections/Apps/NewEvaluation/Controller/YJTEvaDetailVC.m +++ b/yjtim/Sections/Apps/NewEvaluation/Controller/YJTEvaDetailVC.m @@ -28,7 +28,7 @@ @interface YJTEvaDetailVC ()<YJTEvaDateSelectVCDelegate> @property (nonatomic, strong) NSArray *catListArray; @property (nonatomic, strong) YJTEvaSchoolYearModel *evaSchoolYearModel; - +@property (nonatomic, copy) NSString *formId; @property (nonatomic, copy) NSString *className; @property (nonatomic, copy) NSString *classId; @property (nonatomic, copy) NSString *tempClassId; @@ -262,6 +262,7 @@ - (void)rightBtnClick { editVC.evaListModel = self.evaListModel; editVC.catListArray = self.catListArray; ...
方法三
git log --stat 6b81a31033
或者git log 6b81a31033 --stat
查看简洁文件变化
commit 6b81a31033074ef279049e9cbf5944e3158d7510 Author: ZhiChao <[email protected]> Date: Fri Apr 14 13:57:22 2017 +0800 综合评价fixbug yjtim/Sections/Apps/NewEvaluation/Controller/YJTEvaDetailVC.m | 5 +++-- yjtim/Sections/Apps/NewEvaluation/Controller/YJTEvaEditVC.h | 1 + yjtim/Sections/Apps/NewEvaluation/Controller/YJTEvaEditVC.m | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-)
7.忽略跟踪
git checkout . 清空所有更改 以下命令是我们在项目中已经添加了.gitignore 但是中途突然不想再跟踪某文件 此时发现简单的在.gitignore文件中添加要忽略的文件是不起效的,因为该文件已 经被track,我们还需要将其状态改为 未track(其实只需删除暂缓区文件然后将操作体检即可) git rm --cached Podfile.lock 将Podfile.lock从暂缓区删除,不再跟踪
8.备份
备份的话好一些,每次新的版本打一次tag,查看起来还是挺方便。不过不用也行。我不爱使用这个。
git tag -a WeChat1.0 -m "version 1.0" :给版本打上标签 git tag : 查看所有的标签 git push origin WeChat1.0 : 将WeChat1.0 push 到默认分支
9.版本回退
git reset --hard HEAD // 没有提交的情况下进行版本回退 git reset --hard HEAD^ // 回退到上一个版本 git reset --hard HEAD^^ // 回退到指定回退到某个版本 git reset --hard 版本号(至少前5位) // 回退到前几个版本 git reset --hard~1 git revert c011eb3c20ba6fb38cc94fe5a8dda366a3990c61 // 注意该行命令 reset和revert有本质区别
注意:开发中一般托管代码到远程代码仓库,比如Github或则OSChina,加入本地已经使用git push到远程代码仓库,在本地使用git reset回退再push明显是不可行的。因为git reset直接回退到历史中的某个Hash值,但是使用git revert就不一样了。git revert将作为一次新提交(新的Hash值)而不是历史中的某个Hash值,此时再push到远程代码仓库情理之中。
10.日志
想回顾下提交历史,可以使用 git log 命令,其中有个--pretty 参数可以配置git log --pretty=oneline
git log --pretty=format:"%h - %an, %ar : %s"
git log --pretty=format:"%h %s" --graph
git log --since=2.weeks
自己可以试一下,这里附上参数说明,自己可以随意配置,当然log字体颜色也都是可以修改的,不再细述。
选项 | 说明 |
---|---|
%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 | 仅显示指定时间之后的提交。 |
--until, --before | 仅显示指定时间之前的提交。 |
--author | 仅显示指定作者相关的提交。 |
--committer | 仅显示指定提交者相关的提交。 |
你一定奇怪_作者(author)_和_提交者(committer)_之间究竟有何差别,其实作者指的是实际作出修改 的人,提交者指的是最后将此工作成果提交到仓库的人。所以,当你为某个项目发去补丁,然后某个核心成员将你的补丁并入项目时,你就是作者,而那个核心成员就是提交者。我们会在第五章再详细介绍两者之间的细致差别。
11.忽略某些文件
文件 .gitignore 的格式规范如下:
• 所有空行或者以注释符号 # 开头的行都会被 Git 忽略。
• 可以使用标准的 glob 模式匹配。
• 匹配模式最后跟反斜杠(/)说明要忽略的是目录。
• 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号(!)取反。
所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。星号(*)匹配零个或多个任意字符;[abc] 匹配 任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c);问号(?) 只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配 (比如 [0-9] 表示匹配所有 0 到 9 的数字)。
# 此为注释 – 将被 Git 忽略 *.[oa] Git 忽略所有以 .o 或 .a 结尾的文件。一般这类对象文件和存档文件都是编译过程中出现 的,我们用不着跟踪它们的版本 *~ Git 忽略所有以波浪符(~)结尾的文件,许多文本编辑软件 (比如 Emacs)都用这样的文件名保存副本 *.a # 忽略所有 .a 结尾的文件 !lib.a # 但 lib.a 除外 /TODO # 仅仅忽略项目根目录下的 TODO 文件,不包括 subdir/TODO build/ # 忽略 build/ 目录下的所有文件 doc/*.txt # 会忽略 doc/notes.txt 但不包括 doc/server/arch.txt
更多 Git 教程系列文章:
Git 的详细介绍:请点这里
Git 的下载地址:请点这里