ubuntu上的安装
$ sudo apt install git
安装正常输入git会显示帮助信息,或查询版本
$ git --version
git version 2.7.4
进入一个你需要管理版本的项目的目录,可以是一个已经存在的项目目录,或者是一个空的新建项目的目录,执行以下命令:
$ git init
则在项目目录下创建一个.git目录,隐藏目录,存放git相关内容
~/src/.git$ ls
branches COMMIT_EDITMSG config description HEAD hooks index info logs objects refs
$ git status
位于分支 master
初始提交
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
LinkList.cpp
LinkList.h
LinkList.o
lttest.cpp
lttest.o
makefile
tm
提交为空,但是存在尚未跟踪的文件(使用 "git add" 建立跟踪)
列出项目的文件,颜色为红色,表示没有被git跟踪,如果需要对某个文件或多个文件的修改情况进行跟踪,就需要用git add把其添加到工作区
$ git add LinkList.h
$ git status
位于分支 master
初始提交
要提交的变更:
(使用 "git rm --cached <文件>..." 以取消暂存)
新文件: LinkList.h
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
LinkList.cpp
LinkList.o
lttest.cpp
lttest.o
makefile
tm
被跟踪的文件添加到工作区,颜色为绿色。
如果这是在文件中加了一行数字“11111111111”
$ git status
位于分支 master
初始提交
要提交的变更:
(使用 "git rm --cached <文件>..." 以取消暂存)
新文件: LinkList.h
尚未暂存以备提交的变更:
(使用 "git add <文件>..." 更新要提交的内容)
(使用 "git checkout -- <文件>..." 丢弃工作区的改动)
修改: LinkList.h
未跟踪的文件:
(使用 "git add <文件>..." 以包含要提交的内容)
LinkList.cpp
可以提交修改、放弃修改、放弃暂存,通过git diff查看修改的内容。
$ git diff
diff --git a/LinkList.h b/LinkList.h
index fd78947..be0175c 100644
--- a/LinkList.h
+++ b/LinkList.h
@@ -1,6 +1,6 @@
#include
#include
-
+111111111111111111
//自定义数据类型
绿色,前有加号表示改动的地方。
$ git commit LinkList.h -m "增加了一行数字"
[master (根提交) 3e14eae] 增加了一行数字
1 file changed, 23 insertions(+)
create mode 100644 LinkList.h
-m 用于mark你做的变动,备忘录的作用,非常重要,需简明扼要。
$ git log
commit 3e14eaeea1ab0253683200fb755b055a2867432a
Author: gsl371 @here>
Date: Sun Feb 4 21:10:20 2018 +0800
增加了一行数字
再修改一次,提交后的状态。
$ git log
commit 003272447618762684a652fb828b6ad82ecd2987
Author: gsl371
Date: Sun Feb 4 21:19:07 2018 +0800
又增加了一行22222
commit 3e14eaeea1ab0253683200fb755b055a2867432a
Author: gsl371
Date: Sun Feb 4 21:10:20 2018 +0800
增加了一行数字
查看简要信息
$ git log --pretty=oneline
003272447618762684a652fb828b6ad82ecd2987 又增加了一行22222
3e14eaeea1ab0253683200fb755b055a2867432a 增加了一行数字
如果要回退到第一次修改后
git reset --hard 3e14eaeea1ab0253683200fb755b055a2867432a
HEAD 现在位于 3e14eae 增加了一行数字
进入文件,可以看到第二次修改没有了。
如果不想回退了,返回回退前的状态,git reflog查看head
$ git reflog
3e14eae HEAD@{0}: reset: moving to 3e14eaeea1ab0253683200fb755b055a2867432a
0032724 HEAD@{1}: commit: 又增加了一行22222
3e14eae HEAD@{2}: commit (initial): 增加了一行数字
git reset –hard 查到的head号
git reset --hard 0032724
HEAD 现在位于 0032724 又增加了一行22222
先注册github账号,由于你的本地Git仓库和github仓库之间的传输是通过SSH加密的,所以需要一点设置:
第一步:创建SSH Key。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有id_rsa和id_rsa.pub这两个文件,如果有的话,直接跳过此如下命令,如果没有的话,打开命令行,输入如下命令:
$ ssh-keygen -t rsa -b 4096 -C "[email protected]"
Generating public/private rsa key pair.
Enter file in which to save the key (/home/gsl/.ssh/id_rsa):
Created directory '/home/gsl/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
邮箱是你注册github时使用的邮箱,执行的结果生成~/.ssh目录,其中含id_rsa是私钥,不能泄露出去,id_rsa.pub是公钥,可以放心地告诉任何人。
第二步:登录github,打开your profile->personal setting->SSH and GPG key->new ssh key,填上任意title,在Key文本框里黏贴id_rsa.pub文件的内容
echo "# condition-var" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add condition-var https://github.com/gsl371/condition-var.git
git push -u condition-var master
git init 用git初始化一个项目,即本地git管理的操作,见上。
git add README.md 添加一个本项目的说明文件,方便以后查看。
git commit -m “first commit”本次提交的注释。
以上都是本地操作,如果已经做过,可以跳过。
git remote add condition-var https://github.com/gsl371/condition-var.git在远程创建了一个condition-var的仓库 网址是https://github.com/gsl371/condition-var.git
最后一步,把本地的git推送到github 作为master。
以下是执行结果。
Username for 'https://github.com': gsl371
Password for 'https://[email protected]':
对象计数中: 6, 完成.
Delta compression using up to 8 threads.
压缩对象中: 100% (6/6), 完成.
写入对象中: 100% (6/6), 27.26 KiB | 0 bytes/s, 完成.
Total 6 (delta 0), reused 0 (delta 0)
To https://github.com/gsl371/condition-var.git
* [new branch] master -> master
分支 master 设置为跟踪来自 condition-var 的远程分支 master。
推送一个已经存在的项目,后两句即可。
git remote add origin https://github.com/gsl371/condition-var.git
git push -u origin master
由于远程库是空的,我们第一次推送master分支时,加上了 –u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。推送成功后,可以立刻在github页面中看到远程库的内容已经和本地一模一样了。
以后有了变更,只要本地作了提交,就可以通过如下命令:
git push origin master
创建你自己的共享 Git 服务器非常简单,而且在很多情况下,遇到的这点麻烦是完全值得的。不仅仅是因为它保证你有权限查看自己的代码,它还可以通过扩展为 Git 的使用敞开了一扇大门,例如个人 Git 钩子、无限制的数据存储、和持续集成与分发(CI & CD)。
如果你知道如何使用 Git 和 SSH,那么你已经知道怎么创建一个 Git 服务器了。Git 的设计方式,就是让你在创建或者 clone 一个仓库的时候,就完成了一半服务器的搭建。然后允许用 SSH 访问仓库,而且任何有权限访问的人都可以使用你的仓库作为 clone 的新仓库的基础。
但是,这是一个小的点对点环境(ad-hoc)。按照一些方案你可以创建一些带有同样的功能的设计优良的 Git 服务器,同时有更好的拓展性。
首要之事:确认你的用户们,现在的用户以及之后的用户都要考虑。如果你是唯一的用户那么没有任何改动的必要。但是如果你试图邀请其他的代码贡献者使用,那么你应该允许一个专门的分享系统用户给你的开发者们。
假定你有一个可用的服务器(如果没有,这不成问题,Git 会帮忙解决,CentOS 的 树莓派 3 是个不错的开始),首先,必须安装了git,然后第一步就是只允许使用 SSH 密钥认证的 SSH 登录。这比使用密码登录安全得多,因为这可以免于暴力破解,也可以通过直接删除用户密钥而禁用用户。
一旦你启用了 SSH 密钥认证,创建 gituser 用户。这是给你的所有授权的用户们的公共用户:
$ su -c 'adduser gituser'
然后切换到刚创建的 gituser 用户,创建一个 ~/.ssh 的框架,并设置好合适的权限。这很重要,如果权限设置得太开放会使自己所保护的 SSH 没有意义。
$ su - gituser
$ mkdir .ssh && chmod 700 .ssh
$ touch .ssh/authorized_keys
$ chmod 600 .ssh/authorized_keys
authorized_keys 文件里包含所有你的开发者们的 SSH 公钥,你开放权限允许他们可以在你的 Git 项目上工作。他们必须创建他们自己的 SSH 密钥对然后把他们的公钥给你。复制公钥到 gituser 用户下的 authorized_keys 文件中。例如,为一个叫 Bob 的开发者,执行以下命令:
$ cat ~/path/to/id_rsa.bob.pub >> /home/gituser/.ssh/authorized_keys
或者在客户端
$ ssh-copy-id -i ~/.ssh/id_rsa.pub gituser@192.168.122.24
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/gsl/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
gituser@192.168.122.24's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh '[email protected]'"
and check to make sure that only the key(s) you wanted were added.
只要开发者 Bob 有私钥并且把相对应的公钥给你,Bob 就可以用 gituser 用户访问服务器。
但是,你并不是想让你的开发者们能使用服务器,即使只是以 gituser 的身份访问。你只是想给他们访问 Git 仓库的权限。因为这个特殊的原因,Git 提供了一个限制的 shell,准确的说是 git-shell。以 root 身份执行以下命令,把 git-shell 添加到你的系统中,然后设置成 gituser 用户的默认 shell。
# grep git-shell /etc/shells || su -c "echo `which git-shell` >> /etc/shells"
# su -c 'usermod -s git-shell gituser'
现在 gituser 用户只能使用 SSH 来 push 或者 pull Git 仓库,并且无法使用任何一个可以登录的 shell。你应该把你自己添加到和 gituser 一样的组中,在我们的样例服务器中这个组的名字也是 gituser。
举个例子:
# usermod -a -G gituser gsl
仅剩下的一步就是创建一个 Git 仓库。因为没有人能在服务器上直接与 Git 交互(也就是说,你之后不能 SSH 到服务器然后直接操作这个仓库),所以创建一个空的仓库 。如果你想使用这个放在服务器上的仓库来完成工作,你可以从它的所在处 clone 下来,然后在你的 home 目录下进行工作。
严格地讲,你不是必须创建这个空的仓库;它和一个正常的仓库一样工作。但是,一个空的仓库没有工作分支(working tree) (也就是说,使用 checkout 并没有任何分支显示)。这很重要,因为不允许远程使用者们 push 到一个有效的分支上(如果你正在 dev 分支工作然后突然有人把一些变更 push 到你的工作分支,你会有怎么样的感受?)。因为一个空的仓库可以没有有效的分支,所以这不会成为一个问题。
你可以把这个仓库放到任何你想放的地方,只要你想要放开权限给用户和用户组,让他们可以在仓库下工作。千万不要保存目录到比如说一个用户的 home 目录下,因为那里有严格的权限限制。保存到一个常规的共享地址,例如 /opt 或者 /usr/local/share。
以 root 身份创建一个空的仓库:
# git init --bare /opt/jupiter.git
# chown -R gituser:gituser /opt/jupiter.git
# chmod -R 770 /opt/jupiter.git
现在任何一个用户,只要他被认证为 gituser 或者在 gituser 组中,就可以从 jupiter.git 库中读取或者写入。在本地机器尝试以下操作:
$ git clone [email protected]:/opt/jupiter.git
Cloning into 'jupiter.clone'...
Warning: you appear to have cloned an empty repository.
谨记:开发者们一定要把他们的 SSH 公钥加入到 gituser 用户下的 authorized_keys 文件里,或者说,如果他们有服务器上的用户(如果你给了他们用户),那么他们的用户必须属于 gituser 用户组。
运行你自己的 Git 服务器最赞的一件事之一就是可以使用 Git 钩子。Git 托管服务有时提供一个钩子类的接口,但是他们并不会给你真正的 Git 钩子来让你访问文件系统。Git 钩子是一个脚本,它将在一个 Git 过程的某些点运行;钩子可以运行在当一个仓库即将接收一个 commit 时、或者接受一个 commit 之后,或者即将接收一次 push 时,或者一次 push 之后等等。
这是一个简单的系统:任何放在 .git/hooks 目录下的脚本、使用标准的命名体系,就可按设计好的时间运行。一个脚本是否应该被运行取决于它的名字; pre-push 脚本在 push 之前运行,post-receive 脚本在接受 commit 之后运行等等。这或多或少的可以从名字上看出来。
脚本可以用任何语言写;如果在你的系统上有可以执行的脚本语言,例如输出 ‘hello world’ ,那么你就可以这个语言来写 Git 钩子脚本。Git 默认带了一些例子,但是并不有启用。
想要动手试一个?这很简单。如果你没有现成的 Git 仓库,首先创建一个 Git 仓库:
$ mkdir jupiter
$ cd jupiter
$ git init .
然后写一个 “hello world” 的 Git 钩子。因为我为了支持老旧系统而使用 tsch,所以我仍然用它作为我的脚本语言,你可以自由的使用自己喜欢的语言(Bash,Python,Ruby,Perl,Rust,Swift,Go):
$ echo "#\!/bin/tcsh" > .git/hooks/post-commit
$ echo "echo 'POST-COMMIT SCRIPT TRIGGERED'" >> ~/jupiter/.git/hooks/post-commit
$ chmod +x ~/jupiter/.git/hooks/post-commit
现在测试它的输出:
$ echo "hello world" > foo.txt
$ git add foo.txt
$ git commit -m 'first commit'
! POST-COMMIT SCRIPT TRIGGERED
[master (root-commit) c8678e0] first commit
1 file changed, 1 insertion(+)
create mode 100644 foo.txt
现在你已经实现了:你的第一个有功能的 Git 钩子。
Git 钩子最流行的用法就是自动 push 更改的代码到一个正在使用中的产品级 Web 服务器目录下。这是摆脱 FTP 的很好的方式,对于正在使用的产品保留完整的版本控制,整合并自动化内容的发布。
如果操作正确,网站发布工作会像以前一样很好的完成,而且在某种程度上,很精准。Git 真的好棒。我不知道谁最初想到这个主意,但是我是从 Emacs 和 Git 方面的专家,IBM 的 Bill von Hagen 那里第一次听到它的。他的文章包含关于这个过程的权威介绍:Git 改变了分布式网页开发的游戏规则。
每一个 Git 钩子都有一系列不同的变量对应触发钩子的不同 Git 行为。你需不需要这些变量,主要取决于你写的程序。如果你只是需要一个当某人 push 代码时候的通用邮件通知,那么你就不需要什么特殊的东西,甚至也不需要编写额外的脚本,因为已经有现成的适合你的样例脚本。如果你想在邮件里查看 commit 信息和 commit 的作者,那么你的脚本就会变得相对麻烦些。
Git 钩子并不是被用户直接执行,所以要弄清楚如何收集可能会混淆的重要信息。事实上,Git 钩子脚本类似于其他的脚本,像 BASH、Python、C++ 等等一样从标准输入读取参数。不同的是,我们不会给它提供这个输入,所以,你在使用的时候,需要知道可能的输入参数。
在写 Git 钩子之前,看一下 Git 在你的项目目录下 .git/hooks 目录中提供的一些例子。举个例子,在这个 pre-push.sample 文件里,注释部分说明了如下内容:
# $1 -- 即将 push 的远程仓库的名字
# $2 -- 即将 push 的远程仓库的 URL
# 如果 push 的时候,并没有一个命名的远程仓库,那么这两个参数将会一样。
#
# 提交的信息将以下列形式按行发送给标准输入
#
并不是所有的例子都是这么清晰,而且关于钩子获取变量的文档依旧缺乏(除非你去读 Git 的源码)。但是,如果你有疑问,你可以从线上其他用户的尝试中学习,或者你只是写一些基本的脚本,比如 echo 1, 1 , 2, $3 等等。
我发现,对于生产环境来说有一个共同的需求,就是需要一个只有在特定分支被修改之后,才会触发事件的钩子。以下就是如何跟踪分支的示例。
首先,Git 钩子本身是不受版本控制的。 Git 并不会跟踪它自己的钩子,因为对于钩子来说,它是 Git 的一部分,而不是你仓库的一部分。所以,Git 钩子可以监控你的 Git 服务器上的一个空仓库的 commit 记录和 push 记录,而不是你本地仓库的一部分。
我们来写一个 post-receive(也就是说,在 commit 被接受之后触发)钩子。第一步就是需要确定分支名:
#!/bin/tcsh
foreach arg ( $< )
set argv = ( $arg )
set refname = $1
end
这个 for 循环用来读入第一个参数 1,然后循环用第二个参数 1 , 然 后 循 环 用 第 二 个 参 数 2 去覆盖它,然后用第三个参数 $3 再这样。在 Bash 中有一个更好的方法,使用 read 命令,并且把值放入数组里。但是,这里是 tcsh,并且变量的顺序可以预测的,所以,这个方法也是可行的。
当我们有了 commit 记录的 refname,我们就能使用 Git 去找到这个分支的供人看的名字:
set branch = `git rev-parse --symbolic --abbrev-ref $refname`
echo $branch #DEBUG
然后把这个分支名和我们想要触发的事件的分支名关键字进行比较:
if ( "$branch" == "master" ) then
echo "Branch detected: master"
git \
--work-tree=/path/to/where/you/want/to/copy/stuff/to \
checkout -f $branch || echo "master fail"
else if ( "$branch" == "dev" ) then
echo "Branch detected: dev"
Git \
--work-tree=/path/to/where/you/want/to/copy/stuff/to \
checkout -f $branch || echo "dev fail"
else
echo "Your push was successful."
echo "Private branch detected. No action triggered."
endif
给这个脚本分配可执行权限:
$ chmod +x ~/jupiter/.git/hooks/post-receive
现在,当一个用户提交到服务器的 master 分支,那些代码就会被复制到一个生产环境的目录,提交到 dev 分支则会被复制到另外的地方,其他分支将不会触发这些操作。
同时,创造一个 pre-commit 脚本也很简单。比如,判断一个用户是否在他们不该 push 的分支上 push 代码,或者对 commit 信息进行解析等等。
Git 钩子也可以变得复杂,而且它们因为 Git 的工作流的抽象层次不同而变得难以理解,但是它们确实是一个强大的系统,让你能够在你的 Git 基础设施上针对所有的行为进行对应的操作。如果你是一个 Git 重度用户,或者一个全职 Git 管理员,那么 Git 钩子是值得学习的,只有当你熟悉这个过程,你才能真正掌握它。
在我们这个系列下一篇也是最后一篇文章中,我们将会学习如何使用 Git 来管理非文本的二进制数据,比如音频和图片。
如下:
mkdir: XX (创建一个空目录 XX指目录名)
pwd: 显示当前目录的路径。
git init 把当前的目录变成可以管理的git仓库,生成隐藏.git文件。
git add XX 把xx文件添加到暂存区去。
git commit –m “XX” 提交文件 –m 后面的是注释。
git status 查看仓库状态
git diff XX 查看XX文件修改了那些内容
git log 查看历史记录
git reset –hard HEAD^ 或者 git reset –hard HEAD~ 回退到上一个版本
(如果想回退到100个版本,使用git reset –hard HEAD~100 )
cat XX 查看XX文件内容
git reflog 查看历史记录的版本号id
git checkout – XX 把XX文件在工作区的修改全部撤销。
git rm XX 删除XX文件
git remote add origin <网址> 关联一个远程库
git push –u(第一次要用-u 以后不需要) origin master 把当前master分支推送到远程库
git clone <网址> 从远程库中克隆
git checkout –b dev 创建dev分支 并切换到dev分支上
git branch 查看当前所有的分支
git checkout master 切换回master分支
git merge dev 在当前的分支上合并dev分支
git branch –d dev 删除dev分支
git branch name 创建分支
git stash 把当前的工作隐藏起来 等以后恢复现场后继续工作
git stash list 查看所有被隐藏的文件列表
git stash apply 恢复被隐藏的文件,但是内容不删除
git stash drop 删除文件
git stash pop 恢复文件的同时 也删除文件
git remote 查看远程库的信息
git remote –v 查看远程库的详细信息
git push origin master Git会把master分支推送到远程库对应的远程分支上