모든 자식을 포함하는 분기 리베이스
다음 Git 저장소 토폴로지가 있습니다.
A-B-F (master)
\ D (feature-a)
\ /
C (feature)
\
E (feature-b)
feature
분기 를 리베이스함으로써 전체 하위 트리 (하위 분기 포함) 를 리베이스 할 것으로 예상했습니다.
$ git rebase feature master
A-B-F (master)
\ D (feature-a)
\ /
C (feature)
\
E (feature-b)
그러나 이것은 실제 결과입니다.
C' (feature)
/
A-B-F (master)
\ D (feature-a)
\ /
C
\
E (feature-b)
다음을 실행하여 수동으로 쉽게 수정할 수 있다는 것을 알고 있습니다.
$ git rebase --onto feature C feature-a
$ git rebase --onto feature C feature-b
그러나 모든 하위 / 하위 항목을 포함하여 분기를 자동으로 리베이스하는 방법이 있습니까?
git branch --contains C | \
xargs -n 1 \
git rebase --committer-date-is-author-date --preserve-merges --onto B C^
몇 년 전에 저는 이런 일을 처리하기 위해 무언가를 썼습니다. (개선에 대한 의견은 물론 환영하지만 너무 많이 판단하지 마십시오. 오래 전이었습니다! 아직 Perl을 몰랐습니다!)
이는보다 정적 인 상황을위한 것 branch.<branch>.autorebaseparent
입니다. config 매개 변수를 설정하여 구성 합니다. 해당 구성 매개 변수가 설정되지 않은 분기는 건드리지 않습니다. 그것이 당신이 원하는 것이 아니라면, 당신은 큰 문제없이 그것을 원하는 곳에 해킹 할 수있을 것입니다. 지난 1 ~ 2 년 동안 실제로 많이 사용하지는 않았지만 사용했을 때 대량 자동 리베이스로 가능하다면 항상 매우 안전하고 안정적인 것처럼 보였습니다.
여기 있습니다. .NET Framework git-auto-rebase
에서 라는 파일에 저장하여 사용 하십시오 PATH
. -n
실제로 시도하기 전에 드라 이런 ( ) 옵션 을 사용하는 것도 좋은 생각 입니다. 실제로 원하는 것보다 조금 더 자세 할 수 있지만 리베이스를 시도 할 내용과 내용을 보여줄 것입니다. 슬픔을 덜어 줄 수 있습니다.
#!/bin/bash
CACHE_DIR=.git/auto-rebase
TODO=$CACHE_DIR/todo
TODO_BACKUP=$CACHE_DIR/todo.backup
COMPLETED=$CACHE_DIR/completed
ORIGINAL_BRANCH=$CACHE_DIR/original_branch
REF_NAMESPACE=refs/pre-auto-rebase
print_help() {
echo "Usage: git auto-rebase [opts]"
echo "Options:"
echo " -n dry run"
echo " -c continue previous auto-rebase"
echo " -a abort previous auto-rebase"
echo " (leaves completed rebases intact)"
}
cleanup_autorebase() {
rm -rf $CACHE_DIR
if [ -n "$dry_run" ]; then
# The dry run should do nothing here. It doesn't create refs, and won't
# run unless auto-rebase is empty. Leave this here to catch programming
# errors, and for possible future -f option.
git for-each-ref --format="%(refname)" $REF_NAMESPACE |
while read ref; do
echo git update-ref -d $ref
done
else
git for-each-ref --format="%(refname)" $REF_NAMESPACE |
while read ref; do
git update-ref -d $ref
done
fi
}
# Get the rebase relationships from branch.*.autorebaseparent
get_config_relationships() {
mkdir -p .git/auto-rebase
# We cannot simply read the indicated parents and blindly follow their
# instructions; they must form a directed acyclic graph (like git!) which
# furthermore has no sources with two sinks (i.e. a branch may not be
# rebased onto two others).
#
# The awk script checks for cycles and double-parents, then sorts first by
# depth of hierarchy (how many parents it takes to get to a top-level
# parent), then by parent name. This means that all rebasing onto a given
# parent happens in a row - convenient for removal of cached refs.
IFS=$'\n'
git config --get-regexp 'branch\..+\.autorebaseparent' | \
awk '{
child=$1
sub("^branch[.]","",child)
sub("[.]autorebaseparent$","",child)
if (parent[child] != 0) {
print "Error: branch "child" has more than one parent specified."
error=1
exit 1
}
parent[child]=$2
}
END {
if ( error != 0 )
exit error
# check for cycles
for (child in parent) {
delete cache
depth=0
cache[child]=1
cur=child
while ( parent[cur] != 0 ) {
depth++
cur=parent[cur]
if ( cache[cur] != 0 ) {
print "Error: cycle in branch."child".autorebaseparent hierarchy detected"
exit 1
} else {
cache[cur]=1
}
}
depths[child]=depth" "parent[child]" "child
}
n=asort(depths, children)
for (i=1; i<=n; i++) {
sub(".* ","",children[i])
}
for (i=1; i<=n; i++) {
if (parent[children[i]] != 0)
print parent[children[i]],children[i]
}
}' > $TODO
# Check for any errors. If the awk script's good, this should really check
# exit codes.
if grep -q '^Error:' $TODO; then
cat $TODO
rm -rf $CACHE_DIR
exit 1
fi
cp $TODO $TODO_BACKUP
}
# Get relationships from config, or if continuing, verify validity of cache
get_relationships() {
if [ -n "$continue" ]; then
if [ ! -d $CACHE_DIR ]; then
echo "Error: You requested to continue a previous auto-rebase, but"
echo "$CACHE_DIR does not exist."
exit 1
fi
if [ -f $TODO -a -f $TODO_BACKUP -a -f $ORIGINAL_BRANCH ]; then
if ! cat $COMPLETED $TODO | diff - $TODO_BACKUP; then
echo "Error: You requested to continue a previous auto-rebase, but the cache appears"
echo "to be invalid (completed rebases + todo rebases != planned rebases)."
echo "You may attempt to manually continue from what is stored in $CACHE_DIR"
echo "or remove it with \"git auto-rebase -a\""
exit 1
fi
else
echo "Error: You requested to continue a previous auto-rebase, but some cached files"
echo "are missing."
echo "You may attempt to manually continue from what is stored in $CACHE_DIR"
echo "or remove it with \"git auto-rebase -a\""
exit 1
fi
elif [ -d $CACHE_DIR ]; then
echo "A previous auto-rebase appears to have been left unfinished."
echo "Either continue it with \"git auto-rebase -c\" or remove the cache with"
echo "\"git auto-rebase -a\""
exit 1
else
get_config_relationships
fi
}
# Verify that desired branches exist, and pre-refs do not.
check_ref_existence() {
local parent child
for pair in "${pairs[@]}"; do
parent="${pair% *}"
if ! git show-ref -q --verify "refs/heads/$parent" > /dev/null ; then
if ! git show-ref -q --verify "refs/remotes/$parent" > /dev/null; then
child="${pair#* }"
echo "Error: specified parent branch $parent of branch $child does not exist"
exit 1
fi
fi
if [ -z "$continue" ]; then
if git show-ref -q --verify "$REF_NAMESPACE/$parent" > /dev/null; then
echo "Error: ref $REF_NAMESPACE/$parent already exists"
echo "Most likely a previous git-auto-rebase did not complete; if you have fixed all"
echo "necessary rebases, you may try again after removing it with:"
echo
echo "git update-ref -d $REF_NAMESPACE/$parent"
echo
exit 1
fi
else
if ! git show-ref -q --verify "$REF_NAMESPACE/$parent" > /dev/null; then
echo "Error: You requested to continue a previous auto-rebase, but the required"
echo "cached ref $REF_NAMESPACE/$parent is missing."
echo "You may attempt to manually continue from the contents of $CACHE_DIR"
echo "and whatever refs in refs/$REF_NAMESPACE still exist, or abort the previous"
echo "auto-rebase with \"git auto-rebase -a\""
exit 1
fi
fi
done
}
# Create the pre-refs, storing original position of rebased parents
create_pre_refs() {
local parent prev_parent
for pair in "${pairs[@]}"; do
parent="${pair% *}"
if [ "$prev_parent" != "$parent" ]; then
if [ -n "$dry_run" ]; then
echo git update-ref "$REF_NAMESPACE/$parent" "$parent" \"\"
else
if ! git update-ref "$REF_NAMESPACE/$parent" "$parent" ""; then
echo "Error: cannot create ref $REF_NAMESPACE/$parent"
exit 1
fi
fi
fi
prev_parent="$parent"
done
}
# Perform the rebases, updating todo/completed as we go
perform_rebases() {
local prev_parent parent child
for pair in "${pairs[@]}"; do
parent="${pair% *}"
child="${pair#* }"
# We do this *before* rebasing, assuming most likely any failures will be
# fixed with rebase --continue, and therefore should not be attempted again
head -n 1 $TODO >> $COMPLETED
sed -i '1d' $TODO
if [ -n "$dry_run" ]; then
echo git rebase --onto "$parent" "$REF_NAMESPACE/$parent" "$child"
echo "Successfully rebased $child onto $parent"
else
echo git rebase --onto "$parent" "$REF_NAMESPACE/$parent" "$child"
if ( git merge-ff -q "$child" "$parent" 2> /dev/null && echo "Fast-forwarded $child to $parent." ) || \
git rebase --onto "$parent" "$REF_NAMESPACE/$parent" "$child"; then
echo "Successfully rebased $child onto $parent"
else
echo "Error rebasing $child onto $parent."
echo 'You should either fix it (end with git rebase --continue) or abort it, then use'
echo '"git auto-rebase -c" to continue. You may also use "git auto-rebase -a" to'
echo 'abort the auto-rebase. Note that this will not undo already-completed rebases.'
exit 1
fi
fi
prev_parent="$parent"
done
}
rebase_all_intelligent() {
if ! git rev-parse --show-git-dir &> /dev/null; then
echo "Error: git-auto-rebase must be run from inside a git repository"
exit 1
fi
SUBDIRECTORY_OK=1
. "$(git --exec-path | sed 's/:/\n/' | grep -m 1 git-core)"/git-sh-setup
cd_to_toplevel
# Figure out what we need to do (continue, or read from config)
get_relationships
# Read the resulting todo list
OLDIFS="$IFS"
IFS=$'\n'
pairs=($(cat $TODO))
IFS="$OLDIFS"
# Store the original branch
if [ -z "$continue" ]; then
git symbolic-ref HEAD | sed 's@refs/heads/@@' > $ORIGINAL_BRANCH
fi
check_ref_existence
# These three depend on the pairs array
if [ -z "$continue" ]; then
create_pre_refs
fi
perform_rebases
echo "Returning to original branch"
if [ -n "$dry_run" ]; then
echo git checkout $(cat $ORIGINAL_BRANCH)
else
git checkout $(cat $ORIGINAL_BRANCH) > /dev/null
fi
if diff -q $COMPLETED $TODO_BACKUP ; then
if [ "$(wc -l $TODO | cut -d" " -f1)" -eq 0 ]; then
cleanup_autorebase
echo "Auto-rebase complete"
else
echo "Error: todo-rebases not empty, but completed and planned rebases match."
echo "This should not be possible, unless you hand-edited a cached file."
echo "Examine $TODO, $TODO_BACKUP, and $COMPLETED to determine what went wrong."
exit 1
fi
else
echo "Error: completed rebases don't match planned rebases."
echo "Examine $TODO_BACKUP and $COMPLETED to determine what went wrong."
exit 1
fi
}
while getopts "nca" opt; do
case $opt in
n ) dry_run=1;;
c ) continue=1;;
a ) abort=1;;
* )
echo "git-auto-rebase is too dangerous to run with invalid options; exiting"
print_help
exit 1
esac
done
shift $((OPTIND-1))
case $# in
0 )
if [ -n "$abort" ]; then
cleanup_autorebase
else
rebase_all_intelligent
fi
;;
* )
print_help
exit 1
;;
esac
One thing that I've found, since I originally addressed this, is that sometimes the answer is that you didn't actually want to rebase at all! There's something to be said for starting topic branches at the right common ancestor in the first place, and not trying to move them forward after that. But that's between you and your workflow.
If it is need to update a committer date, the GIT_COMMITTER_DATE
environment variable can be used (manual). Also use --format
option to get a branch name without additional formatting.
export GIT_COMMITTER_DATE=$( date -Iseconds )
git branch --format='%(refname)' --contains C | xargs -n 1 | git rebase -p --onto master C^
unset GIT_COMMITTER_DATE
# don't forget to unset this variable to avoid effect for the further work
NB: it is required to set either --committer-date-is-author-date
or GIT_COMMITTER_DATE
to guarantee the same checksum for C'
, Ca'
and Cb'
commits (on rebasing feature, feature-a and feature-b correspondingly).
참고URL : https://stackoverflow.com/questions/5600659/rebasing-a-branch-including-all-its-children
'Programing' 카테고리의 다른 글
xcode6 beta 6 osx swift 프로젝트에서 개체 ( 'po')를 인쇄 할 수 없음 : (자동 가져 오기 오류 : AST 컨텍스트에서 모듈 '__ObjC'를 가져 오지 못했습니다) (0) | 2020.09.12 |
---|---|
모든 JVM 플래그 인쇄 (0) | 2020.09.12 |
Facebook에 업로드 된 이미지에 IPTC 메타 데이터가 자동으로 추가됨 (0) | 2020.09.12 |
Vim에서 자동 줄 바꿈 (들여 쓰기 유지) (0) | 2020.09.12 |
cascade = {“remove”} VS orphanRemoval = true VS ondelete = "CASCADE (0) | 2020.09.12 |