Git 常用操作

    Tools

这里记录了常用的 Git 命令和知识,主要用于检索


关于 Git 的一些概念

  • Repository
  • Remote
  • Index / Stage
  • Workspace

安装

通常 Mac 已经自带 Git ,但若要追求最新版,可使用 Hombrew 来安装

$ brew install git

初始化

## 进入需要用 Git 进行追踪以保存各个时间点进度的文件夹
$ cd ~/rails101
## 初始化当前所在文件夹
$ git init

设置

$ git config --global user.name "xxx xxx"        # Git 用户名
$ git config --global user.email "xxx@xxx.com"   # Git 邮箱
$ git config --global color.ui true              # Git 命令的输出文字色彩化

# 显示当前的Git配置
$ git config --list

# Example
$ git config --global user.name "chpwang"        
$ git config --global user.email "chpwang@gmail.com"

代码提交

## 常用基本指令
$ git status            ## 查看当前文件更改状态      
$ git add [文件名]       ## 添加更改文件到 staging area(顶图中的 index 区)
$ git commit -m "该 commit 的注释内容"    ## 保存 staging area 的文件(顶图中的 Repository 区)
$ git log --oneline | head -n 5         ## 查看前 5 个 commit 的信息(一行一个)

## 撤销
$ git checkout [file]            ## 恢复暂存区的指定文件到工作区(working area)
$ git checkout [commit] [file]   ## 恢复指定 commit 的指定文件到工作区
$ git checkout .                 ## 恢复上一个commit的所有文件到工作区
$ git checkout -- [file]         ## 清空工作区(working area)的指定文件的改动(不影响 Staging area)
# push 基本指令
$ git push [远程主机名] [本地分支名]:[远程分支名]

# Example
$ git push heroku ch08:master

# 第一次推送 master 分支时,加上 -u 参数,Git 不但会把本地的 master 分支内容推送的远程新的 master 分支,还会把本地的 master 分支和远程的 master 分支关联起来,在以后的推送或者拉取时就可以简化命令 - git push 和 git pull
$ git push -u origin master

# 推送本地的 chpw-dev 分支到远程主机的 chpw-dev 分支上,并将本地的 chpw-dev 分支设置为追踪(track)远程分支 chpw-dev
# Branch 'chpw-dev' set up to track remote branch 'chpw-dev' from 'origin'
# 下次在本地的 chpw-dev 分支上就可以直接用简单命令 git push 来推送 chpw-dev 分支了
$ git push -u origin chpw-dev:chpw-dev

# 推送本地所有分支
$ git push --all

拉取远程代码

## 从远程主机克隆一个版本库到本地并在本地生成一个目录,与远程主机的版本库同名
$ git clone [版本库的网址]

## Example
$ git clone git@github.com:bitcoin/bitcoin.git

## 若要指定不同的目录名,可以用以下命令
$ git clone [版本库的网址] [本地目录名]

临时缓存

## 缓存当前进度(只能缓存 tracked files,不能缓存 untracked files)- 以下两条命令效果一样
$ git stash
$ git stash save

## 缓存包括 untracked files 在内的所有文件
$ git stash save --include-untracked

## 只缓存 Unstaged Area 的文件改动,保留 Staging Area
$ git stash save --keep-index

## 缓存当前进度的同时增加对该缓存的描述,以便之后恢复时查看
$ git stash save "just add twentynight.html"

## 查看临时缓存里的各种存档
$ git stash list
stash@{0}: WIP on ch02: 5d65fd4 add flash messages at index

## 在当前分支上恢复存档,默认是恢复 stash@{0} 的存档
$ git stash apply [存档ID名]
## Example - 恢复 stash@{1} 的存档
$ git stash apply 1

## 默认删除临时缓存里所保存的最新一条内容,即删除 stash@{0} - stash apply 之后不会自动删除已被 apply 内容,所以需要手动 drop 来删除
$ git stash drop
Dropped refs/stash@{0} (bceb5b826e75881c82f83827605fc81938d49a49)

## 如果不小心 drop 掉了代码,可以用被 drop 掉的 stash 所对应的 ID 重新找回
$ git stash apply bceb5b826e75881c82f83827605fc81938d49a49

## 删除缓存里的所有内容
$ git stash clear

## 以下命令相当于先运行 git stash apply ,再运行 git stash drop
$ git stash pop

重置和读档

参考 git real level 2

## 已经 add 的如何撤销(unstage files)?
$ git reset HEAD <file>...

HEAD 作为指针指向最新的那个 commit,而 HEAD^ 指向倒数第二新的 commit,HEAD^^ 指向倒数第三新的 commit,以此类推,HEAD^^^^^ 有 5 个 ^(caret),所以它指向倒数第六新的 commit

## 已经 commit 的如何撤销?
$ git reset --soft HEAD^   ## 把最新的 commit 里的修改撤销到 Stage ,文件成为 Staging files,HEAD 指针进而指向倒数第二新的 commit

$ git reset --hard HEAD^   ## 慎用该命令!它会把最新的 commit 里的修改完全撤销,所有修改完全消失,HEAD 指针进而指向倒数第二新的 commit

## 如何清除 Working Area(工作台)里所有已改动的文件,恢复到 HEAD (最新一个 commit)的状态
## 可以先 Stash 缓存,然后再清空缓存
$ git stash save --include-untracked
$ git stash drop

更改已有 Commit - Interactive Rebase

下面展示了最新的 3 条 Commit 信息:

$ git log --oneline | head -n 3
a58f305 arrange folders for next Udacity Class - Deep Learning
cf56d01 fix bugs, misunderstandings and mistakes in P5 Boston Housing - Udacity MLforNBs
eacff8b complete P5 Boston Housing - Udacity MLforNBs

使用命令 git rebase -i HEAD~3 进入一个使用 vi 编辑器打开的文件,编辑这个文件(更改文件中顶部的 Commit 的关键词 - 例如 pick 改成 r 表示要重命名 Commit 的信息 - 然后用 :wq 保存文件退出),可以实现对已有倒数 3 条 Commit 进行编辑更改(HEAD~3 代表倒数 3 条 Commit):

  pick eacff8b complete P5 Boston Housing - Udacity MLforNBs
  pick cf56d01 fix bugs, misunderstandings and mistakes in P5 Boston Housing - Udacity MLforNBs
  pick a58f305 arrange folders for next Udacity Class - Deep Learning

  # Rebase 3e6e796..a58f305 onto 3e6e796 (3 commands)
  #
  # Commands:
  # p, pick = use commit
  # r, reword = use commit, but edit the commit message
  # e, edit = use commit, but stop for amending
  # s, squash = use commit, but meld into previous commit
  # f, fixup = like "squash", but discard this commit's log message
  # x, exec = run command (the rest of the line) using shell
  # d, drop = remove commit
  #
  # These lines can be re-ordered; they are executed from top to bottom.
  #
  # If you remove a line here THAT COMMIT WILL BE LOST.
  #
  # However, if you remove everything, the rebase will be aborted.
  #
  # Note that empty commits are commented out

例子 - 重命名最新一条 Commit 的信息(message):

  1. 将目标 Commit 前的关键词 pick 改成 r :

    pick eacff8b complete P5 Boston Housing - Udacity MLforNBs
    pick cf56d01 fix bugs, misunderstandings and mistakes in P5 Boston Housing - Udacity MLforNBs
    r a58f305 arrange folders for next Udacity Class - Deep Learning
    
    # Rebase 3e6e796..a58f305 onto 3e6e796 (3 commands)
    #
    # Commands:
    # p, pick = use commit
    # r, reword = use commit, but edit the commit message
    # e, edit = use commit, but stop for amending
    # s, squash = use commit, but meld into previous commit
    # f, fixup = like "squash", but discard this commit's log message
    # x, exec = run command (the rest of the line) using shell
    # d, drop = remove commit
    
  2. :wq 保存文件退出(这是 vi 编辑器的保存退出方式);

  3. 在弹出的又一个使用 vi 编辑器打开的文件中,修改 Commit 信息(commit message);

    arrange folders for next Udacity Class - Deep Learning    # <- 修改这行信息
    
    # Please enter the commit message for your changes. Lines starting
    # with '#' will be ignored, and an empty message aborts the commit.
    #
    # Date:      Wed Jul 4 15:59:36 2018 +0800
    #
    # interactive rebase in progress; onto 929522f
    # Last commands done (10 commands done):
    #    pick cf56d01 fix bugs, misunderstandings and mistakes in P5 Boston Housing - Udacity MLforNBs
    #    reword a58f305 arrange folders for next Udacity Class - Deep Learning
    # Next commands to do (5 remaining commands):
    #    pick b02f9b2 add new version of P5 with Python 3+
    #    pick e2e1a5f add data visualized codes in Question 1st
    # You are currently editing a commit while rebasing branch 'master' on '929522f'.
    
  4. :wq 保存文件退出;

  5. 再次查看最新的 3 条 Commit 信息,可以发现刚刚的 Commit 被更改了(Commit 的 Hash 值也变了):

    $ git log --oneline | head -n 3
    670afa6 I changed this commit - arrange folders for next Udacity Class - Deep Learning
    cf56d01 fix bugs, misunderstandings and mistakes in P5 Boston Housing - Udacity MLforNBs
    eacff8b complete P5 Boston Housing - Udacity MLforNBs
    

Interactive Rebase 还可以实现「改变 Commit 的顺序」,「把两个 Commit 合并」还有「删除某个 Commit 」等操作,具体可以参考使用 git rebase -i HEAD~3 命令后,vi 编辑器打开的文件中注释部分的说明(上面的例子中也展示了注释部分)


分支操作

## 切换分支
$ git checkout [分支名]

## 新建分支
$ git checkout -b [分支名]

## 查看所有分支
$ git branch -a [分支名]

## 删除分支(删除 A 分支需要切换到 A 之外的分支上)
$ git branch -d [分支名]   ## 删除已经 merge 过了的分支,通常已经是 merge 到 master 分支上了
$ git branch -D [分支名]   ## 强制删除该分支,无论该分支是否 merge 到其他分支过

git checkout 的另一种操作(实现从之前的 commit 处新发展一条分支):

## 切换到某一个 commit
$ git checkout [Commit ID]

## Example
$ git branch
  master
* chpw-dev

## 查看 Commit ID
$ git log
commit 86d2b225d27f3ba7fc06164c9961a98a08e3f525 (HEAD -> chpw-dev)
Author: abc <abc@gmail.com>
Date:   Tue Aug 7 15:40:29 2018 +0800

    add senario that location info is unauthorized

commit c9e39a3975dcd2d380a7436bd8e3c4bba34cb0b0
Author: abc <abc@gmail.com>
Date:   Tue Aug 7 13:57:28 2018 +0800

    get and show current location on all pages

## 根据上面的 ID 信息,切换到前一个 Commit
$ git checkout c9e39a3975dcd2d380a7436bd8e3c4bba34cb0b0
Note: checking out 'c9e39a3975dcd2d380a7436bd8e3c4bba34cb0b0'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at c9e39a3... get and show current location on all pages

## 切换后处于一个 detached HEAD
$ git branch
* (HEAD detached at c9e39a3)
  master
  chpw-dev

## 以当前的 detached HEAD 为起点,新建分支
$ git checkout -b newBranchFromPreCommit
Switched to a new branch 'newBranchFromPreCommit'

$ git branch
  master
  chpw-dev
* newBranchFromPreCommit

恢复被删除的 Git 分支

参考 Can I recover a branch after its deletion in Git?


远程同步

生成 SSH 验证 - 廖雪峰

$ cd ~   ## 切换到 home 目录下
$ ssh-keygen -t rsa -C "youremail@example.com"
$ git remote add [远程主机名] [地址]

## 显示所有远程仓库
$ git remote -v

## 移除本地记录的远程主机
$ git remote rm [远程主机名]

设置远程


.gitignore

关于文件 .gitignore 的用法,参考这里


Git Submodule

新建,更新和同步 Submodule 参考多台电脑间的 Hexo 博客同步中 Submodule 功能部分的说明


更新从别人那 Fork 来的 Repository

假设我们 Fork 了 Repo cisco/ChezScheme,接下来可以使用下面的步骤获取最新更新,进而更新我们 Fork:

  1. 从自己的账号上下载 Fork 过来的 Repo:

    $ git clone git@github.com:chpwang/ChezScheme.git
    
  2. 设置 Repo 的 remote:

    $ pwd
    ~/ChezScheme   ## 当前处于了刚 Clone 下来的 Repo 目录
    
    ## 目前存在一个名为 origin 的 remote ,指向自己账号旗下的项目 Fork
    $ git remote -v
    origin  git@github.com:chpwang/ChezScheme.git (fetch)
    origin  git@github.com:chpwang/ChezScheme.git (push)
    
    ## 添加原始 Repo 的 remote 地址(下面的代码将其命名为 upstream)
    ## 命令为 git remote add [upstream_name] [path_to_repo]
    $ git remote add upstream git@github.com:cisco/ChezScheme.git
    
    ## 添加新 remote 之后,一共存在两个 remote ,一个名为 origin 指向自己的 Repo ,一个名为 upstream 指向原始 Repo
    $ git remote -v
    origin  git@github.com:chpwang/ChezScheme.git (fetch)
    origin  git@github.com:chpwang/ChezScheme.git (push)
    upstream  git@github.com:cisco/ChezScheme.git (fetch)
    upstream  git@github.com:cisco/ChezScheme.git (push)
    
  3. 从原始 Repo 下载更新(新的 Commit)然后合并在本地的 Repo 对应分支上(下面例子是 master 分支):

    $ git pull upstream master
    
  4. 上传本地更新后的代码到自己账户下 Fork 过来的 Repo ,进而实现更新:

    $ git push origin master
    

打赏