Git 使用记录 - 基础

作者: orientlu | 来源:发表于2016-08-07 22:07 被阅读518次

    @(版本控制)[git]

    大四开始使用git协同开发,到现在工作一年,从一开始使用模模糊糊,每次遇到冲突都胆战心惊,到如今慢慢感受到这个分布式控制工具给开发带来的便利。越发觉得,掌握工具、使用工具提高效率的重要性!

    个人开发环境 ubuntu 14.04

    说明:

    • $ 表示终端执行命令
    • 命令注释

    • [] 表示可选

    Git 是一个分布式版本控制工具

    分布式版本控制分布式版本控制

    基本上,使用git工作的流程如下:

    • 修改文件,在工作目录中修改文件。
    • 暂存文件,将文件的快照放入暂存区域
    • 提交更新,找到暂存区域的文件,将快照永久性存储到Git 仓库目录
    • 推送到远程服务器

    对应以上流程,git 的三个状态关系如下


    git stategit state

    安装配置 Git

    安装 git

    命令行直接输入git提示应用没有安装的情况下
    安装git,[图形化 gitk, 差异比较工具 meld]

    $ sudo apt-get install git gitk meld
    

    其他平台参考

    配置 git

    • 在~目录下添加一个.gitignore,将需要忽略的对象添加进来,对当前用户所有仓库生效。
    • 配置config,使用 --global 说明对当前用户所有仓库生效(对特定仓库或者文件的配置,可以使用 --local/ --file)。
    $ touch ~/.gitignore
    $ echo "cscope*" > ~/.gitignore
    $ echo "tag*" >> ~/.gitignore
    $ echo "*.swp" >> ~/.gitignore
    $ git config --global core.excludesfile ~/.gitignore
    
    # 自报家门,记得替换自己的名称和邮箱...
    $ git config --global user.name luchaodong
    $ git config --global user.email lcdsdream@126.com
    
    # 显示配置
    $ git config --list
    

    说明:

    • /etc/gitconfig 文件:系统中对所有用户都普遍适用的配置。若使用 git config 时用 --system 选项,读写的就是这个文件。
    • ~/.gitconfig 文件:用户目录下的配置文件只适用于该用户。若使用 git config 时用 --global 选项,读写的就是这个文件。
    • 当前项目的 Git 目录中的配置文件(也就是工作目录中的 .git/config 文件):这里的配置仅仅针对当前项目有效。若使用 git config 时用 --local选项,读写的就是这个文件。

    每一个级别的配置都会覆盖上层的相同配置,所以 .git/config 里的配置会覆盖/etc/gitconfig 中的同名变量。

    开始使用 Git

    版本库创建

    现有目录使用git开始版本管理

    创建 .git 后将需要跟踪的文件添加并提交。
    通过.gitignore设定不想跟踪的文件,再执行 git add --all, 把其他所有文件加入追踪目录。

    $ git init
    $ git add --all 
    $ git commit -m "initial project version"
    
    # 取消追踪某个文件
    $ git rm --cached <file>
    

    克隆现有仓库

    获取仓库链接,保存在本地 local_pySerial 目录下

    git clone git@github.com:lcdsdream/pySerial.git  [local_pySerial]
    

    .gitignore 例子

    # no .a files
    *.a
    
    # but do track lib.a, even though you're ignoring .a files above
    !lib.a
    
    # only ignore the TODO file in the current directory, not subdir/TODO
    /TODO
    
    # ignore all files in the build/ directory
    build/
    
    # ignore doc/notes.txt, but not doc/server/arch.txt
    doc/*.txt
    
    # ignore all .pdf files in the doc/ directory
    doc/**/*.pdf
    

    修改追踪

    修改提交

    # 开始写啊写
    $ git add file_a                # 将修改过的内容加入暂存区
    
    $ git commit                    # 将暂存区的修改提交到本地仓库
    $ git commit -a                 # 将暂存区和工作区的修改提交到本地目录
                                    # -m "提交说明"
    

    修改记录查看

    $ git status                    # 查看文件状态
    $ git log                       # 查看提交记录
    
    $ git diff                      # 查看工作区修改内容
    $ git diff --cached             # 查看暂存区修改内容
    $ git diff HEAD                 # 查看当前工作区和暂存区修改的内容
    
    $ git blame file                # 查看每一行代码谁提交的
    

    撤销操作

    撤销修改

    # 取消修改暂存
    $ git add file_a                # 将file_a 修改保存到暂存区后想取消,
    # 执行
    $ git reset HEAD file_a         # 将file_a的修改从暂存区推出
    
    # 取消修改
    # 想直接取消工作区文件的修改
    # 执行 , 慎重!
    $ git checkout file_a           # 撤销工作目录下的修改
    
    # 重新提交
    $ git  commit
    # 发现漏了一文件
    $ git add forgotten_file
    $ git commit --amend
    

    撤销错误提交

    # 回退到某次提交, 修改的内容仍然保留在工作区(--mixed)
    # 如此操作修改了git 的commit 和index,如果推到服务器的,有冲突!
    
    $ git reset [--mixed] commit_SHA
    
    # --soft
    # 保留源码,只回退到commit 信息到某个版本.不涉及index的回退,如果还需要提交,直接commit即可.
    # --hard
    # 回退,不保留修改!!!!!
    
    # 在原来错误的提交基础上,执行反转
    # commit 一直保持向前,不会带来冲突
    # revert 时要求工作树干净
    
    $ git revert commit_SHA
    

    打标签

    以示重要,打个标签

    查看

    $ git tag                       # 列出标签
    $ git tag -l 'v1.8.5*'           # 列出感兴趣部分标签
    

    打标签

    Git 使用两种主要类型的标签:轻量标签(lightweight)与附注标签(annotated)。
    * 轻量标签很像一个不会改变的分支,它只是一个特定提交的引用。

    ```bash
    $ git tag v1.4-lw
    # 提供标签名称,无其他参数
    ```
    
    * 附注标签是存储在 Git 数据库中的一个完整对象。
    

    它们是可以被校验的;其中包含打标签者的名字、电子邮件地址、日期时间;还有一个标签信息;并且可以使用 GNU Privacy Guard (GPG)签名与验证。

    ```bash
    # -m "标签信息", 否则会运行编辑器要求输入
    $ git tag -a v1.4 -m "my version 1.4" 
    
    # 查看标签信息
    $ git show v1.4
    ```
    

    通常建议创建附注标签,这样你可以拥有以上所有信息;但是如果你只是想用一个临时的标签,或者因为某些原因不想要保存那些信息,轻量标签也是可用的。

    $ git tag -a v1.2 commit_SHA       # 給某个提交打标签
    
    $ git push origin v1.4              # 把标签推到共享服务器
    $ git push origin --tags            # 推送多个标签
    

    Git 分支

    Git 通过一个名为 HEAD 的特殊指针指向当前所在的本地分支(将 HEAD 想象为当前分支的别名)。


    git 分支git 分支

    创建切换合并分支

    $ git branch testing                # 在当前提交对象创建一个分支
    $ git branch testing xx             # 从XX(标签,SHA等创建分支)
    $ git checkout testing              # 切换到testing分支
    
    $ git checkout -b testing           # 创建分支并切换过去
    
    # 查看分支
    $ git branch                        # -a 查看所有分支,包括远程
                                        # -v 查看分支的最后提交
    
    # 在新分支上进行开发并有一些新提交,测试后合并回主分支
    $ git checkout master
    $ git merge testing
    
    # 删除临时分支
    $ git branch -d testing             # -D 强制删除,如果没有merge的情况下
    

    分支切换,是HEAD指针指向对象的改变, HEAD 指向当前分支

    多人协同开发的情况,对同一个文件同一个部分进行了修改,导致git没法干净地进行合并,这时候会产生冲突。
    如下,合并的时候 tt.c 出现冲突

    $ git merge testing
    Auto-merging tt.c
    CONFLICT (content): Merge conflict in tt.c
    Automatic merge failed; fix conflicts and then commit the result.
    
    $ git status
    On branch master
    You have unmerged paths.
      (fix conflicts and run "git commit")
    
    Unmerged paths:
      (use "git add <file>..." to mark resolution)
    
        both modified:      tt.c
    
    no changes added to commit (use "git add" and/or "git commit -a")
    

    使用编辑工具打开冲突文件进行手动合并。每一个冲突的部分,git都会标记出来

    <<<<<<< HEAD:tt.c
    printf("1");
    printf("21");
    =======
    printf("1");
    printf("11");
    >>>>>>> testing:tt.c
    

    如上,<<<<<<< HEAD:index.html
    =======的之间的内容是当前分支(这里是master)的内容,而后面到
    >>>>>>> testing:index.html之间是testing分支的内容。

    解决完冲突后暂存(告知git冲突解决),最后执行git commit,完成合并。

    如何解决冲突?

    根据实际需要,保留HEAD或者新合并内容,亦或者,两者的结合。这个需要根据实际情况判断。

    git 分支合并

    压合合并

    在A上进行添加功能测试,有多次提交,最后完成合并到分支B,只需作为一个记录提交

    $ git checkout B
    $ git meger --squash A
    # 此时A中的所有修改会加入到B中,但是没有提交,保留在暂留区
    $ git  commit -m "xx" 
    # 统一提交
    

    拣选合并

    拣选 另外分支的某一个提交

    $ git  checkout B
    $ git cherry-pick SHA
    

    拣选多个提交

    git cherry-pick -n  SHA1
    # 会把合并保存到暂存区
    git cherry-pick -n  SHA..
    # 继续合并后提交
    
    git commit
    # 不添加留言默认使用最后一条,或者接下去编辑
    

    Git 变基

    昨天我从master检出分支开发新功能A, 今天master上有新功能加入,我想A功能基于今天的master上开发,所以执行变基。
    变基就像重新播放一样,把master今天的修改移到我昨天检出的分支上。
    一般,变基使提交历史更加简洁,减少分叉。

    变基-1变基-1

    experiment 分支在C2的基础上修改了C4,但是master今天修改了C3,希望experiment的C4切到C3上,通过变基实现。

    $ git checkout experiment
    $ gti rebase master
    
    变基-2变基-2

    之后将修改提交到master分支,master分支执行快进合并

    $ git checkout master
    $ git merger experiment
    
    变基-3变基-3

    同样的提交记录,对比直接merge,提交历史更加好看。


    变基-4变基-4

    合并后有分叉

    Git 与远程仓库

    查看远程URL

    $ git remote -v
    origin  git@github.com:lcdsdream/pySerial.git (fetch)
    origin  git@github.com:lcdsdream/pySerial.git (push)
    

    增删改远程库

    # 添加 url
    $ git remote add new_fe git@github.com:lcdsdream/pySerial.git
    # 重命名
    $ git remote rename new_fe old_fe
    # 删除
    $ git remote remove fe
    

    获取远程库

    # 获取远程到本地的origin/xxx, 需要手动合并到本地的xxx
    $ git fetch
    
    # 获取远程到本地的origin/xxx, 自动合并到本地的xxx
    $ git pull 
    
    # 获取远程某个分支
    $ git checkout -b 本地分支名 远程分支名(比如origin/xx)
    
    

    推送到远程仓库

    推送master分支到远程仓库

    $ git push origin master
    

    删除远程分支

    # 方法1 
    $ git push origin --delete serverfix
    
    # 方法2 推送空分支替代
    $ git push origin :serverfix
    

    ssh 访问远程仓库

    通过ssh访问github等远程代码库。

    # 查看是否已经有秘钥
    $ ls -al ~/.ssh
    
    # 生成ssh key
    $ ssh-keygen -t rsa -C "your_email@example.com"
    # 回车默认即可。
    
    # 添加key到ssh-agent
    # 启动ssh-agent
    $ ssh-agent -s
    $ ssh-add ~/.ssh/id_rsa
    
    # 若执行ssh-add /path/to/xxx.pem是出现这个错误:
    # Could not open a connection to your authenticationagent
    # 则先执行如下命令即可:
    $ ssh-agent bash
    
    

    将公钥加入到github中

    github_rsagithub_rsa

    测试

    $ ssh -T git@github.com
    # The authenticity of host 'github.com (207.97.227.239)' can't be established.# RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.# Are you sure you want to continue connecting (yes/no)?
    $ yes
    # Hi username! You've successfully authenticated, but GitHub does not# provide shell access.
    

    测试通过!

    储藏

    在一个分支开发,由于某些需求需要切换到另一个分支,但是对于当前的内容还不想提交,需要用到储藏功能。

    $ git stash                     # 将工作区和暂存区修改储藏, 入栈
    $ git stash --keep-index        # 将工作区内容存储
    
    $ git stash list                # 列出储藏内容
    
    # 应用最后一次储藏的修改,(但是对应还保留在栈中)
    # 不指定stash@{}的话,默认取出最后推进内容
    # --index, 恢复的时候原来暂存的修改会暂存
    $ git stash apply stash@{x}
    
    # 应用最后一次储藏的修改(对应清除),出栈
    $ git stash pop stash@{x}
    
    $ git stash drop      # 直接删除储藏的修改
    
    
    # 储藏后由于修改可能无法直接取出
    # 新建分支,合并
    $ git  stash branch branch_name
    

    Git patch

    使用git format-patch生成所需要的patch:

    • 当前分支所有超前master的提交:
    $ git format-patch -M master
    
    • 某次提交以后的所有patch:
      不包括该次提交。
    $ git format-patch SHA
    
    • 从根到指定提交的所有patch:
      包括该次提交
    $ git format-patch --root SHA
    
    • 某两次提交之间的所有patch:
      包括指定的那两次提交
    git format-patch 365a..4e16 --365a和4e16分别对应两次提交的名称
    
    • 某次提交(含)之前的几次提交:
      比如-2, 则该次提交和该提交的上一次
    git format-patch –n SHA --n指patch数
    
    • 单次提交即为:
    git format-patch -1 SHA
    

    git format-patch生成的补丁文件默认从1开始顺序编号,并使用对应提交信息中的第一行作为文件名。
    如果使用了 --numbered-files选项,则文件名只有编号,不包含提交信息;
    如当所有patch输出到一个文件;可指定-o path指定patch的存放目录;

    检查

    # 检查patch文件
    $ git apply --stat newpatch.patch
    # 检查能否应用成功:
    $ git apply --check newpatch.patch
    #不行会报错
    

    打补丁

    $ git am --signoff < newpatch.patch
    # (使用-s或--signoff选项,可以commit信息中加入Signed-off-by信息)
    

    应用patch出现问题:

    一个典型的git am失败,可能是这样的:

    $ git am < PATCH
    Applying: PACTH DESCRIPTION
    error: patch failed: file.c:137
    error: file.c: patch does not apply
    error: patch failed: Makefile:24
    error: libavfilter/Makefile: patch does not apply
    Patch failed at 0001 PATCH DESCRIPTION
    When you have resolved this problem run "git am --resolved".
    If you would prefer to skip this patch, instead run "git am --skip".
    To restore the original branch and stop patching run "git am --abort".
    

    处理

    $ git apply PATCH --reject
    # 打开文件处理
    # (译注:根据.rej文件手动解决所有冲突)
    $ git add file_xxx
    $ git am --resolved
    

    end

    后续补充.....
    补充-如何撤销修改操作


    参考

    Pro git boot

    相关文章

      网友评论

        本文标题:Git 使用记录 - 基础

        本文链接:https://www.haomeiwen.com/subject/gsilsttx.html