我怎样在Git中做到以下操作?
我的当前分支是branch1,我已经进行了一些本地更改。然而,我现在意识到我实际上想要将这些更改应用于branch2。有没有办法将这些更改应用/合并到branch2中,使它们成为branch2上的本地更改,而不必在branch1上提交它们?
我怎样在Git中做到以下操作?
我的当前分支是branch1,我已经进行了一些本地更改。然而,我现在意识到我实际上想要将这些更改应用于branch2。有没有办法将这些更改应用/合并到branch2中,使它们成为branch2上的本地更改,而不必在branch1上提交它们?
由于您的文件尚未在 branch1
中提交:
git stash
git checkout branch2
git stash pop
或者git stash
git checkout branch2
git stash list # to check the various stash made in different branch
git stash apply x # to select the right one
上面是更长、更明确的版本,源自rbento的回答。git stash
git stash branch branch2
这里使用了:
git stash branch <branchname> [<stash>]
- 创建并检出一个名为
<branchname>
的新分支,该分支从最初创建<stash>
时的提交开始- 将
<stash>
中记录的更改应用于新的工作树和索引。如果成功,并且
<stash>
是如下格式的引用stash@{<revision>}
,那么它会删除<stash>
。如果在运行
git stash push
的分支上发生了足够多的更改,导致git stash apply
由于冲突而失败,则此功能很有用。
由于stash条目应用于运行git stash
时的HEAD所在的提交之上,因此它可以恢复最初存储的状态,而没有冲突。
如benjohn的评论所述(请参见git stash
手册页面):
要同时隐藏当前未跟踪(新添加的)文件,请添加参数
-u
,因此:
git stash -u
git stash -u
。 - Benjohn暂存、临时提交和变基可能都过于复杂。如果你还没有将改变的文件加入到索引中,那么你可能只需要切换到其他的分支。
git checkout branch2
只要你在branch1和branch2之间编辑的文件没有不同,这样做就可以。它会让你保留工作中的更改并切换到branch2。如果它们不同,那么你可以使用-m
选项指定要将本地更改与切换分支引入的更改合并。
git checkout -m branch2
如果你已经将更改添加到索引中,那么你需要先使用重置撤消这些更改。(这将保留你的工作副本,只会删除暂存的更改。)
git reset
checkout -m
不是“安全的”(可能会导致合并冲突),那么存储是否提供任何优势(例如,您可以取消弹出的存储)? - Craig McQueen更简单的替代方案如下:
将更改暂存到一个stash中。
git stash
创建并切换到一个新分支,然后一步将stash应用到该分支上。
git stash branch 新分支名称
接着,只需将更改添加(add
)和提交(commit
)到这个新分支即可。
警告:不适合git新手。
在我的工作流程中,这个问题出现的频率足以让我尝试编写一个新的git命令。通常使用git stash
流程是正确的方法,但有点笨拙。我通常首先创建一个新的提交,因为如果我一直在查看更改,所有信息都会在我的脑海中清晰,最好立即开始对我发现的内容(通常是在功能分支上工作时发现的属于主分支的修复程序)进行git commit
。
如果您经常遇到这样的情况,拥有与当前工作目录并排的另一个工作目录始终检出了
master
分支也很有帮助。
所以我是这样做到的:
git commit
提交更改。git reset HEAD~1
撤消当前分支的提交。稍后(异步)或立即在另一个终端窗口中:
cd my-project-master
这是另一个共享相同的.git
的工作目录。git reflog
查找刚刚创建的修复程序。git cherry-pick SHA1
提交的哈希值。可选(仍然异步)您可以重新基于分支以获取错误修复(通常在准备提交PR并已经清理了功能分支和工作目录时):
cd my-project
这是我正在使用的主要WD。git rebase master
获取错误修复。这样我就可以持续地开发该功能,无需担心使用 git stash
或在执行 git checkout
前清空我的工作目录(然后再次检查功能分支),并且所有的错误修复都会进入 master
,而不是隐藏在我的功能分支中。
在我看来,当你正在处理一些大型功能时,使用 git stash
和 git checkout
真是让人头疼。
my-project-master
和.git
共享让人感觉像是这样。为什么不使用git checkout -b bugfixABC; git commit -a; git reset HEAD^ --hard
,然后稍后(异步地)在master
上,git cherry-pick <SHA1 of the commit(s) in bugfixABC>
呢?(甚至可以为了避免找到SHA1而直接使用git rebase --onto master feature bugfixABC
,从你当前所在的任何分支开始。这意味着你可以在上面的git reset
之后直接在feature
上执行此操作。) - Gauthiercheckout -m
即可。 - Gauthier如果你想处理提交的更改,你应该查看git-rebase,但正如VonC在评论中指出的那样,由于你所说的是本地更改,因此使用git-stash肯定是一个好的方法。
您可以使用以下命令检查状态和所在分支:
注意:如果在转移到新分支之前在本地 repo 中进行更改,则以下步骤仍应有效。
如果 "git branch" 显示 master,并且您想要创建并移动到另一个分支:
再次使用 "git branch" 检查分支,现在应该显示您在新分支中。
现在添加、提交和推送:
以上步骤对我来说在两种情况下都适用,即在转移到新本地分支之前或在转移到新分支后进行更改。希望这能帮助遇到类似情况的人。
git stash
。另外,你可以从这个链接学习https://git-scm.com/docs/git-stash。git stash
git checkout branch2
git stash pop
我发现这个答案很有用。
然而,由于该线程已关闭且无法发表评论,我对该答案有疑问。
当我应用git checkout other_branch
时,我遇到了以下错误
error: pathspec 'other_branch' did not match any file(s) known to git
所以,我不是应用命令,而是使用了“which”来解决我的问题。
git branch other_branch
git checkout other_branch
当您创建了一个新文件时。例如,在文件资源管理器中,VSCode会在文件旁边显示一个 U
。
当您对之前提交到存储库中的文件进行更改时。
所以想象一下,您当前在分支 A
上,但您只想将对现有文件的更改提交到分支 A
,而新创建的文件(未跟踪)应该提交到一个新的分支 B
。可以使用一些技巧来使用暂存功能,逐步解释如下。
.git/config
添加脚本在 .git
文件夹内有一个 config
文件。打开它,您会看到类似于以下内容:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[remote "origin"]
url = https://github.com/...
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
将配置文件更改为:
[core]
repositoryformatversion = 0
filemode = true
bare = false
logallrefupdates = true
ignorecase = true
precomposeunicode = true
[alias]
stash-untracked = "!f() { \
git stash; \
git stash -u; \
git stash pop stash@{1}; \
}; f"
[remote "origin"]
url = https://github.com/...
fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
remote = origin
merge = refs/heads/main
现在当你在A分支上时,你将能够使用以下命令。
git stash-untracked
如果您正在使用诸如VSCode之类的编辑器,您将会看到新文件消失了(现在已被隐藏)
在仍然处于A分支的情况下,将更改提交到现有文件:
git add .
git commit -m "committing tracked changes to current branch"
checkout -b
命令可以立即访问它)。git checkout -b newBranchName
使用 stash pop
命令,存储的更改将被添加到当前分支。
git stash pop
现在唯一剩下的就是将更改提交到新分支B上并进行提交
git add .
git commit -m "created new file"
警告:不适合 Git 初学者。
与chakrit's answer类似,我经常遇到这种情况:在一个功能分支上工作,发现一个 bug 并想修复它。但是修复应该在main
分支上进行,而不是my-feature
。要将更改内容放入main
中的整个序列是7个或更多个git
命令,这真的很烦人且容易出错。
由于我找不到脚本来完成此操作,所以我自己编写了它。只需将其放在$PATH
的某个位置(例如/usr/local/bin
或/$HOME/.local/bin
或其他位置),然后您可以执行以下操作:
# currently working on branch `my-feature`
$ git add some-file # use git add -p if you want only some changes
$ git commit-branch main --rebase -m 'Fixed some nasty bug in some-file'
然后它将打印一些进度消息:
Committing your staged changes to branch 'main'.
+ git checkout --quiet HEAD~0
+ git commit --quiet -m 'Fixed some nasty bug in some-file'
++ git rev-parse HEAD
+ commit_hash=82513091473646a09d541893b8bd60a0f98b765d
+ git stash push --quiet
+ git checkout --quiet main
+ git cherry-pick --quiet 82513091473646a09d541893b8bd60a0f98b765d
[main 1c5d96e] Fixed some nasty bug in some-file
Date: Mon Feb 6 15:04:03 2023 +0100
1 file changed, 2 insertions(+)
+ git checkout --quiet my-feature
+ git rebase --quiet main
+ git stash pop --quiet
+ set +x
Success.
这是文件git-commit-branch
的源代码。请不要忘记在将其放置在$PATH
后执行chmod +x
。该脚本也可以在github上找到:https://github.com/fritzw/git-utils。欢迎提出改进意见。
它的工作方式如下:
如果任何命令失败,它将简单地停止并打印一些信息以帮助您恢复情况。如果您想了解更多详细信息,请查看脚本末尾的注释和 git 命令序列。
#!/usr/bin/env bash
set -o errexit
set -o nounset
usage() {
echo "Usage: git commit-branch <target-branch> [--rebase|-r] [ <git-commit-options>... ]"
echo ""
echo "Commits your staged changes to <target-branch>, discarding them from your current branch."
echo "Use --rebase or -r to rebase your current branch on the new commit in <target-branch>,"
echo "and thus include the changes in your current branch as well."
echo ""
echo "Example usage working on branch my-feature:"
echo " git add some-file"
echo " git commit-branch main --rebase -m 'Fixed a bug in some-file'"
}
if [[ $# -lt 1 ]]; then
usage
exit 1
fi
target_branch="$1"; shift # Remove first argument
if ! git rev-parse --verify "$target_branch" >/dev/null; then
echo "fatal: '$target_branch' is not a branch in this git repository."
usage
exit 1
fi
rebase_command=''
if [[ $# -gt 0 ]] && [[ "$1" == "-r" || "$1" == "--rebase" ]]; then
rebase_command="git rebase --quiet $target_branch"
shift # Remove -r/--rebase argument
fi
current_branch="$(git branch --show-current)"
if ! [[ "$current_branch" ]]; then
echo "fatal: Unable to determine current branch. You must be on a branch to use git commit-branch."
exit 1
fi
commit_hash='not-committed-yet'
print_error_message() {
set +x
echo
echo "Something went wrong in the last command. :-("
echo "Your unstaged changes and untracked files should be in the last stash."
echo "Your previously staged changes should be in the following commit: $commit_hash"
echo "Please check which commands were executed and try to undo them manually."
echo
}
echo "Committing your staged changes to branch '$target_branch'."
trap 'print_error_message' ERR # Print some hopefully helpful info if something fails
set -x # Print all executed commands
git checkout --quiet 'HEAD~0' # Go into 'detached HEAD' state to avoid changing current branch
git commit --quiet "$@" # Create temporary commit
commit_hash="$(git rev-parse HEAD)" # Note temporary commit ID
git stash push --include-untracked --quiet # Save all other changes from working tree
git checkout --quiet "$target_branch" # Move to target branch
git cherry-pick --quiet "$commit_hash" # Apply changes from temporary commit to target branch
git checkout --quiet "$current_branch" # Switch back to current branch
$rebase_command # Execute git rebase if --rebase flag is present
git stash pop --quiet # Re-apply untracked changes to working tree
set +x # Stop printing executed commands
echo "Success."
if ! [[ "$rebase_command" ]]; then
echo ""
echo "If you want to include those changes in your current branch, you can run:"
echo " git stash; git rebase $target_branch; git stash pop"
echo "or"
echo " git stash; git merge $target_branch; git stash pop"
echo ""
fi