跳转至

Introduction to Git Commands

导言

Git命令的基本使用。

Git相关概念

Git管理的三个区域

  1. Working Tree 当前的工作区域
  2. Index/Stage 暂存区域,和git stash命令暂存的地方不一样。使用git add xx,就可以将xx添加近Stage里面
  3. Repository 提交的历史,即使用git commit提交后的结果

git传输数据使用的协议

有4种,

  • 本地协议(Local),
  • HTTP 协议,
  • SSH(Secure Shell)协议
  • Git 协议。

但是Local与SSH主要使用在自己搭建的git环境里。这里只介绍其余两种。

http协议

Git 通过 HTTP 通信有两种模式。 在 Git 1.6.6 版本之前只有一个方式可用,十分简单并且通常是只读模式的。 Git 1.6.6 版本引入了一种新的、更智能的协议,让 Git 可以像通过 SSH 那样智能的协商和传输数据。

类似 GitHub 的服务,你在网页上看到的 URL(比如 https://github.com/schacon/simplegit)智能 HTTP 的运行方式和 SSH 及 Git 协议类似,只是运行在标准的 HTTP/S 端口上并且可以使用各种 HTTP 验证机制, 这意味着使用起来会比 SSH 协议简单的多,比如可以使用 HTTP 协议的用户名/密码授权,免去设置 SSH 公钥。

git协议

Git 里的一个特殊的守护进程;它监听在一个特定的端口(9418),类似于 SSH 服务,但是访问无需任何授权。 使用与 SSH 相同的数据传输机制,但是省去了加密和授权的开销。

什么是origin

可以理解成本地文件夹的名字-u, --set-upstream set upstream for git pull/status

什么是HEAD

HEAD就是当前活跃分支的游标。

形象的记忆就是:你现在在哪儿,HEAD就指向哪儿,所以Git才知道你在那儿!

gitconfig

第一次使用需要设置用户信息

# global对当前用户所有仓库
git config --global user.name  "shaojiemike"  
git config --global user.email  "[email protected]"

# local对当前仓库s
git config user.name  "username"  
git config user.email  "email"

使用git config --global --list查看

[user]
        name =  aaa
        email = aaa.github.com
[alias]
        last = log -1
        co = checkout
        cm = commit
        rb = rebase
        br = branch
        rt = remote
        st = status
        lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit -15
  lg-graph = log --oneline --graph --decorate --all
[filter "lfs"]
        clean = git-lfs clean -- %f
        smudge = git-lfs smudge -- %f
        process = git-lfs filter-process
        required = true

git命令用法

  • git diff
    • 相对于常用的git status, 也是十分有用的。
  • git commit:
#修正上一次的commit信息
git commit -amend  -m "新的提交信息" 
git push --force
git reset HEAD~ #不小心commit额外文件,但是还没有push时,回退。
  • git clone xxx.git “指定目录”
  • git fetch
git fetch -p origin

    -p, --prune           清除远程已经不存在的分支的跟踪分支
    -P, --prune-tags      清除远程不存在的本地标签,并且替换变更标签

合并冗余无效commit

来合并冗余无效的commit, 参考这篇例子很详细

git rebase -i cf7e875 

# 其余地方同步修改的git:重置本地分支到远程
# 同步远程仓库的最新状态。
git fetch origin
# 强制将本地的 branch_name 分支重置为远程的状态。
git reset --hard origin/branch_name
10 个提交中选择性地将其中的两个合并,其他的保持不变
  1. 查看最近的 10 个提交:

    git log --oneline -n 10
    
    这会列出最近的 10 个提交。假设输出如下:
    a1b2c3d Commit 10
    b2c3d4e Commit 9
    c3d4e5f Commit 8
    d4e5f6g Commit 7
    e5f6g7h Commit 6
    f6g7h8i Commit 5
    g7h8i9j Commit 4
    h8i9j0k Commit 3
    i9j0k1l Commit 2
    j0k1l2m Commit 1
    

  2. 假设你想将 Commit 5Commit 6 合并,而其他提交保持不变。执行以下命令:

    git rebase -i HEAD~10
    

  3. 这会打开一个交互式的文本编辑器,显示最近 10 次提交:

    pick a1b2c3d Commit 10
    pick b2c3d4e Commit 9
    pick c3d4e5f Commit 8
    pick d4e5f6g Commit 7
    pick e5f6g7h Commit 6
    pick f6g7h8i Commit 5
    pick g7h8i9j Commit 4
    pick h8i9j0k Commit 3
    pick i9j0k1l Commit 2
    pick j0k1l2m Commit 1
    

  4. 将你想合并的提交标记为 squash(或 s),确保第一个保留为 pick,其他设置为 squash。例如:

    pick a1b2c3d Commit 10
    pick b2c3d4e Commit 9
    pick c3d4e5f Commit 8
    pick d4e5f6g Commit 7
    pick e5f6g7h Commit 6
    squash f6g7h8i Commit 5
    pick g7h8i9j Commit 4
    pick h8i9j0k Commit 3
    pick i9j0k1l Commit 2
    pick j0k1l2m Commit 1
    

  5. 保存并退出编辑器。Git 会将 Commit 5Commit 6 合并为一个提交,并让你编辑合并后的提交信息。

  6. 运行 git log --oneline,你将看到提交历史已经更新,Commit 5Commit 6 被合并为一个。

  7. 最后,将更改推送到远程仓库(可能需要强制推送):

    git push origin branch-name --force
    

通过这种方式,你可以在最近的 10 个提交中挑选两个进行合并,而无需合并所有提交。

10 个提交中任意选择若干合并
  • git rebase -i HEAD~10 后,将代表commit的那行任意移动即可;前后乱序会自动触发后面的Merge Conflict Resolution。

删除某些commit

rebase 删除或者修改某些commit

在处理合并(merge commit)后发现有不需要的提交时,你可以通过几种方式来删除或修改这些提交。具体的操作方法取决于你希望如何处理这些提交:是否仅仅是删除某些提交,还是完全重写历史。

方法 1: git rebase -i(交互式变基)

如果你想删除或修改某些提交,可以使用 git rebase -i 来进行交互式变基。

  1. 找到合适的基准提交: 运行 git log 查找你想要开始重写的提交的哈希(commit hash)。假设你想修改最后的 N 次提交。

  2. 启动交互式变基

    git rebase -i HEAD~N
    
    这里 N 是你希望回溯的提交数。

  3. 编辑提交: 在打开的编辑器中,你会看到类似以下内容:

    pick abcdef1 Commit message 1
    pick abcdef2 Commit message 2
    pick abcdef3 Commit message 3
    ...
    

  4. 如果你想删除某些提交,删除对应的行。

  5. 如果你想修改某个提交(比如更改提交内容或合并提交),可以将 pick 改为 edit,然后按提示修改。
  6. 如果你想将多个提交合并为一个,可以将多个 pick 修改为 squashfixup

  7. 继续 rebase: 保存并关闭编辑器后,Git 会按你编辑的内容进行操作。如果有冲突,Git 会提示你解决冲突并继续执行变基。

  8. 完成 rebase: 一旦 rebase 完成,使用 git log 确认提交历史。

方法 2: git reset(硬重置)

如果你希望回退到某个提交,并丢弃之后的所有提交,可以使用 git reset

  1. 硬重置到某个提交
    git reset --hard <commit_hash>
    

这会将当前分支的 HEAD 重置到指定的提交,丢弃所有之后的提交。请小心,因为这会清除工作区和暂存区的更改。

  1. 强制推送(如果已经推送到远程仓库): 如果这些更改已经推送到远程仓库,并且你希望同步历史,可以强制推送:
    git push origin <branch_name> --force
    

注意:这可能会影响其他与该分支协作的人。

方法 3: git revert(回退提交)

如果不想重写历史,但希望撤销某些提交的内容,可以使用 git revert。这个命令会创建一个新的提交,撤销指定提交的变更。

  1. 选择要撤销的提交: 使用 git log 查找你想撤销的提交的哈希。

  2. 撤销指定的提交

    git revert <commit_hash>
    

如果要撤销多个提交,可以使用 -n 参数批量撤销:

git revert -n <commit_hash1> <commit_hash2> ...

  1. 提交撤销的更改: 完成回退后,Git 会自动创建一个新的提交,撤销指定提交的变更。如果你希望推送到远程仓库,记得执行 git push

总结

  • 删除提交:使用 git rebase -igit reset(如果你愿意丢弃历史)。
  • 撤销提交内容:使用 git revert 来回退变更并保留历史。

具体选择哪种方式取决于你的需求(是否希望保留提交历史等)。

吸收一次git commit

  • git cherry-pick:
    • 无需merge,只移动一个commit到其余分支
  • git patch
    • 来实现一次commit的移动
从别人仓库吸收一次commit

要合并其他人 fork 的主仓库中的某个特定提交,你可以按照以下步骤操作:

  1. 添加远程仓库:首先,你需要将那个别人的 fork 添加为远程仓库。例如,如果他们的 GitHub 用户名是 username,仓库名是 repo,你可以这样添加:
git remote add other-fork https://github.com/username/repo.git
  1. 获取提交:接着,你需要从那个远程仓库获取数据:
git fetch other-fork
  1. 查看提交记录:你可以查看该仓库的提交记录,找到你想要合并的提交的哈希值:
git log other-fork/main  # 假设主分支是 main
  1. 合并特定提交:一旦你找到了想要的提交哈希(例如 abc1234),你可以使用 cherry-pick 命令将该提交合并到你的当前分支:
git cherry-pick abc1234
  1. 解决冲突:如果在合并时出现冲突,你需要手动解决这些冲突,解决后运行:
git cherry-pick --continue
  1. 推送更改:最后,将更改推送到你的远程仓库:
git push origin your-branch-name

这样,你就成功将其他 fork 的某个提交合并到你的仓库了!如果有任何具体问题,随时问我。

只吸收一个commit的部分文件,加选择--no-commit
git cherry-pick --no-commit <commit-hash>
git restore  --staged <path/to/unwanted/unmerged/file>

更改远程仓库

git remote -v #查看
origin  [email protected]:Kirrito-k423/BHive-Prediction-Compare.git (fetch)
origin  [email protected]:Kirrito-k423/BHive-Prediction-Compare.git (push)
# 修改命令
git remote set-url origin [url]
# set to access repository using token
git remote set-url origin https://Kirrito-k423:<access token>@github.com/Kirrito-k423/dokuwiki-data.git
# 先删后加
git remote rm origin
git remote add origin [url]

标签 tags

# 查看标签
git tag

# 添加标签
git tag <tag_name>
git tag -a <tag_name> -m "Tag message"
git tag <tag_name> <commit_hash>

# 删除标签
# .1 删除本地标签
git tag -d <tag_name>
# .2 删除远程标签
git push origin --delete <tag_name>

# push标签
# .1 推送单个标签
git push origin <tag_name>
# .2 推送所有本地标签到远程仓库
git push origin --tags

贮藏与清理 stash

有时,当你在项目的一部分上已经工作一段时间后,所有东西都进入了混乱的状态, 而这时你想要切换到另一个分支做一点别的事情。 问题是,你不想仅仅因为过会儿回到这一点而为做了一半的工作创建一次提交。 针对这个问题的答案是 git stash 命令。1

常见使用场景

  1. git status有还未修改的地方
  2. 运行 git stashgit stash push
  3. 刚刚贮藏的工作重新应用:git stash apply
  4. 其他
    1. 运行 git stash drop 加上将要移除的贮藏的名字来移除它
    2. 运行 git stash pop 来应用贮藏然后立即从栈上扔掉它
git stash:             将当前未commit的信息放入stash栈
git stash pop:         将栈顶的存储信息弹出。
git stash apply:       使用栈顶的存储信息,但是不弹出。
git stash list:        显示栈中列表
git stash save $name  以名字形式进栈
git stash drop $name  按名字索引,删除某个存储信息。
git stash clear:       清空存储信息。

撤销 restore

用于还原工作区的命令,适用于文件的更改,主要用于撤销未提交的更改。

# 恢复 <file> 到暂存区的状态,撤销工作区中的改动。 常用于删除一个文件的修改
git restore <file>

# 取消文件的暂存状态,将文件从暂存区移回工作区。
git restore --staged <file>

删除 rm

# 删除暂存区和工作区的文件
git rm -f 文件名

# 仅仅删除暂存区里的文件    
git rm --cache 文件名

回退 reset

git reset 是一个更为强大的命令,可以更改提交记录、暂存区和工作区的状态。它主要用于回退到某个特定的提交,同时可以选择是否影响暂存区和工作区的更改。

  • 回退到某个特定提交
  • 从暂存区或提交历史中移除更改,不保留提交记录。

常用命令:

  • git reset --soft <commit>:回退到指定提交,保留工作区和暂存区的修改,仅回退提交历史。
  • git reset --mixed <commit>:回退到指定提交,保留工作区的修改,但取消暂存区的修改
  • git reset --hard <commit>:回退到指定提交,清空工作区和暂存区的修改

用法示例:

# 回退到某个提交,保留工作区和暂存区的修改
git reset --soft HEAD~1

# 回退到某个提交,保留工作区的修改,但取消暂存区的修改
git reset --mixed HEAD~1

# 回退到某个提交,并且丢弃工作区和暂存区的修改
git reset --hard HEAD~1

都会重置 HEAD 和 branch。

以下面的例子为例

reset --mixed

默认的选项。

  • 保留工作目录
  • 暂存区清空,并把原节点和reset节点的差异的文件放在工作目录.

总而言之就是,工作目录的修改、暂存区的内容以及由 reset 所导致的新的文件差异,都会被放进工作目录

reset --soft

--soft则会保留工作目录和stage暂存区中的内容, 并把因为保留工作目录内容所带来的新的文件差异放进暂存区

假设此时当前 commit 的改动内容是新增了 laughters.txt 文件:git show --stat

执行回退会发现, 原先 HEAD 处 commit 的改动(也就是那个 laughters.txt 文件)也会被放进暂存区

删除最近的第二次的commit,但是保留最新commit的内容

要删除最近的第二次 commit 但保留最新的 commit 内容,你可以使用以下步骤:

  1. 查看历史记录: 你可以先用以下命令查看最近的 commit 历史,确认你要删除的 commit:

    git log --oneline
    

  2. 软重置到第二次 commit 之前: 假设你要删除的第二次 commit 的哈希值是 abc123,你可以用以下命令将 HEAD 移动到它的父 commit:

    git reset --soft abc123^
    

  3. 重新提交保留的内容: 由于是软重置,工作目录中的文件仍然保持不变,所有修改都处于暂存状态。你可以直接重新提交这些内容:

    git commit -m "Your commit message"
    

  4. 强制推送(如果你在一个远程分支上工作且已经推送过): 如果你已经推送到远程分支,可能需要强制推送:

    git push --force
    

这样操作之后,第二次 commit 就会被删除,最新 commit 的内容会被保留并重新提交。

reset --hard

会重置stage区和工作目录,没有commit的修改会被全部擦掉。(stash的东西应该不会消失)

$ git reset --hard HEAD~3  # 回退上上上一个版本  
$ git reset –hard bae128  # 回退到某个版本回退点之前的所有信息。 
$ git reset --hard origin/master    # 将本地的状态回退到和远程的一样 

git revert

git revert 是一个不破坏提交历史的命令,用于撤销某个提交,但会生成一个新的提交来进行撤销操作。它不会像 git reset 那样删除历史,而是通过逆向应用某次提交的更改来生成新的提交。

  • 撤销已经推送到远程仓库的提交
  • 保持提交历史干净,避免删除历史记录。
# 撤销指定的提交,并创建一个新的提交来反转这些更改。
git revert HEAD~1

分支

创建

决定要解决你的公司使用的问题追踪系统中的 #53 问题。 想要新建一个分支并同时切换到那个分支上,你可以运行一个带有 -b 参数的 git checkout 命令:2

$ git checkout -b iss53
Switched to a new branch "iss53"

它是下面两条命令的简写:

$ git branch iss53
$ git checkout iss53

从某个commit分支

git checkout commitId -b 本地新branchName 

拉远端新分支下来

# 拉取所有远程分支信息到本地,但不会自动切换分支或更新工作目录。
git fetch --all
# 查看分支
git branch -av

# 方法1 默认会在本地建立一个和远程分支名字一样的分支
git checkout feature # 当前没stash的修改会一起移动
git checkout -t origin/feature
# 方法2 将远程的feature 拉到本地feature1
git checkout -b feature1 origin/feature
# 方法3 注意不要fetch到当前分支,会提示:fatal: 拒绝获取到非纯仓库的当前分支
git fetch origin b1:b1

本地覆盖远端某分支

git push origin develop:master
or, more generally
$ git push <remote> <local branch name>:<remote branch to push into>

正常切换

git checkout iss53

分支上提交

git push origin xxx (xxx为要提交代码的分支名称)

合并 Merge

暴力:PR单个Merge Commit

git rebase master

This will merge the history of the “master” branch onto our “dev” branch. You can use the rebase command with any base commit reference

强制将本地的 branch_name 分支重置为远程的状态。

git reset --hard origin/branch_name

分支与主仓库的最新提交同步,并将所有修改合并为一个提交

要将你的分支与主仓库的最新提交同步,并将所有修改合并为一个提交,你可以按照以下步骤进行操作:

  1. 添加主仓库为远程源(如果还没有添加的话):

    git remote add upstream <主仓库URL>
    

  2. 获取主仓库的最新更改

    git fetch upstream
    

  3. 切换到你的分支(假设你的分支名为 my-feature-branch):

    git checkout my-feature-branch
    

  4. 进行变基(rebase)

    git rebase upstream/{master branch}
    

这会将你的提交“移动”到主仓库的最新提交之后。

  1. 将所有提交合并为一个提交: 在变基过程中,如果没有冲突,你可以使用以下命令将多个提交合并为一个:

    git reset --soft upstream/{master branch}
    git add .
    git commit -m "Your combined commit message"
    

  2. 推送到你的远程分支: 如果你之前已经推送了你的分支,你可能需要强制推送:

    git push origin my-feature-branch --force
    

  3. 创建 PR: 在 GitHub 或其他代码托管平台上,从你的分支创建一个 PR。

这样,你就可以将你的所有修改合并为一个提交,并且在主仓库的最新基础上提交 PR。

submodule问题请看另一篇文章

技巧:分支间复制修改

要将 v2.1.0 分支上的修改移到 v2.3.0 分支

使用 git cherry-pick 命令。以下是具体步骤:

  1. 切换到 v2.3.0 分支
git checkout v2.3.0
  1. 找出 v2.1.0 上的提交

使用 git log 查看 v2.1.0 上的提交记录,找到你想要移到 v2.3.0 的提交哈希(commit hash)。

git checkout v2.1.0
git log
  1. v2.3.0 上 cherry-pick 提交

使用 git cherry-pick 命令将选择的提交应用到 v2.3.0。假设你要移植的提交哈希为 abc1234,你可以这样做:

git checkout v2.3.0
git cherry-pick abc1234

如果有多个提交,你可以依次执行 cherry-pick,或者使用提交范围:

git cherry-pick <start_commit>^..<end_commit>
  1. 解决冲突(如果有)

如果在 cherry-pick 过程中遇到冲突,Git 会提示你。你需要手动解决这些冲突,然后继续执行:

git add <resolved_files>
git cherry-pick --continue
  1. 完成后推送

修改完成后,推送到远程仓库:

git push origin v2.3.0

这样,v2.1.0 上的修改就会成功移到 v2.3.0 分支上了。

情况1:fast-forward

基于 master 分支的紧急问题分支 hotfix branch 你可以运行你的测试,确保你的修改是正确的,然后将 hotfix 分支合并回你的 master 分支来部署到线上。 你可以使用 git merge 命令来达到上述目的:

$ git checkout master
$ git merge hotfix

想要合并的分支 hotfix 所指向的提交 C4 是你所在的提交 C2 的直接后继, 因此 Git 会直接将指针向前移动。换句话说,当你试图合并两个分支时, 如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候, 只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”。

情况2:解决diverged分歧

你的开发历史从一个更早的地方开始分叉开来(diverged)。 因为,master 分支所在提交并不是 iss53 分支所在提交的直接祖先,Git 不得不做一些额外的工作。 出现这种情况的时候,Git 会使用两个分支的末端所指的快照(C4 和 C5)以及这两个分支的公共祖先(C2),做一个简单的三方合并。

手动解决分歧

git merge //冲突后
git status //显示unmerged的结果

文件格式如下:

<<<<<<< HEAD:index.html
a-----------balabala------------
=======
b-----------balabala------------
>>>>>>> iss53:index.html

只需要修改成想要的,然后 git add。git就会认为git冲突已经被处理。

最好使用 git commit来提交,message应该是默认的 Merge branch 'iss53'

删除

git branch -d hotfix

参考文献

https://www.jianshu.com/p/c2ec5f06cf1a

https://blog.csdn.net/weixin_36572983/article/details/106340607

git restore: https://www.cnblogs.com/teach/p/13997323.html