It's kind weird, but I can't fulfill a pretty common operation with git. Basically what I want is to checkout a feature branch, not using it's head but using SHA id. This SHA points between merges from master branch.
The problem is that all I get is just master branch without a commits from feature branch. Currently I'm trying to fix a regression introduced earlier in master branch.
Just to be more descriptive, I crafted a small bash script to recreate a problem repository:
#!/bin/bash
rm -rf ./.git
git init
echo "test1" > test1.txt
git add test1.txt
git commit -m "test1" -a
git checkout -b patches master
echo "test2" > test2.txt
git add 开发者_如何学Pythontest2.txt
git commit -m "test2" -a
git checkout master
echo "test3" > test3.txt
git add test3.txt
git commit -m "test3" -a
echo "test4" > test4.txt
git add test4.txt
git commit -m "test4" -a
echo "test5" > test5.txt
git add test5.txt
git commit -m "test5" -a
git checkout patches
git merge master
#Now how to get a branch having all commits from patches + test3.txt + test4.txt - test5.txt ???
Basically all I want is just to checkout branch "patches" with files 1-4, but not including test5.txt.
Doing:
git checkout [sha_where_test4.txt_entered]
... just gives a branch with test1,test3,test4, but excluding test2.txt
More complex example:
#!/bin/bash
rm -rf ./.git
git init
echo "test1" > test1.txt
git add test1.txt
git commit -m "test1" -a
git checkout -b patches master
echo "test2" > test2.txt
git add test2.txt
git commit -m "test2" -a
git checkout master
echo "test3" > test3.txt
git add test3.txt
git commit -m "test3" -a
echo "test4" > test4.txt
git add test4.txt
git commit -m "test4" -a
echo "test5" > test5.txt
git add test5.txt
git commit -m "test5" -a
git checkout patches
git merge master
echo "test6" > test6.txt
git add test6.txt
git commit -m "test6" -a
#Now how to get a branch having all commits from patches + test3.txt + test4.txt - test5.txt ???
git log --topo-order | cat
# Now I need something to help me going back to history
# without manually calculating that patches~2 sha's
git checkout -b patches.tmp master~1
git merge patches~2
Thanks.
Regarding your first example, you need to replay test3 and 4 on top of test2: this is a classic case of rebase --onto
:
Start from:
alt text http://img169.imageshack.us/img169/2255/gitr1.png
Mark the current patches emplacement, and move the patches branch where you would like to end (test4):
C:\Prog\Git\tests\rep\main>git checkout patches
Switched to branch 'patches'
C:\Prog\Git\tests\rep\main>git checkout -b tmp
Switched to a new branch 'tmp'
C:\Prog\Git\tests\rep\main>git checkout patches
Switched to branch 'patches'
C:\Prog\Git\tests\rep\main>git reset --hard master~1
HEAD is now at 8448d0f test4
That gives you:
alt text http://img169.imageshack.us/img169/4826/gitr2.png
And just rebase onto what you want the correct sequence of commits:
C:\Prog\Git\tests\rep\main>git checkout tmp
Switched to branch 'tmp'
C:\Prog\Git\tests\rep\main>git rebase --onto tmp tmp~1 patches
First, rewinding head to replay your work on top of it...
Applying: test3
Applying: test4
Which gives:
alt text http://img52.imageshack.us/img52/372/gitr3.png
That only works for linear set of commits to be moved around.
There is no such point. You have two parallel paths of development here, one containing the files test1 and test2, the other containing files test1, test3, test4 and test5. You can create such a point with a merge, though.
Check out the SHA1 for the commit that adds test4, and merge with the commit that adds test2. For example, after running your script my repository looks like this:
* b054987 (HEAD, patches) Merge branch 'master' into patches
|\
* | 5ae790f test2
| * f2a3dac (master) test5
| * 70e8cd2 test4
| * c4102ed test3
|/
* d448eaa test1
On this, run:
% git checkout 70e8c
% git merge 5ae79
The result is a HEAD which contains files 1-4:
* bcc8f7a (HEAD) Merge commit '5ae79' into HEAD
|\
| | * b054987 (patches) Merge branch 'master' into patches
| | |\
| |/ /
| * | 5ae790f test2
| | * f2a3dac (master) test5
| |/
|/|
* | 70e8cd2 test4
* | c4102ed test3
|/
* d448eaa test1
% ls
test1.txt test2.txt test3.txt test4.txt
You can now create a branch from this point of you like.
A git merge
operation doesn't go back and change history, rather it creates a new commit pointing to the two histories that it came from. So, in pictures, what you have is something like this:
2-----------\ / \ 1---3---4---5---M (HEAD)
When you rewind history to go back to revision 4, you get files 1, 3, and 4 but not 2.
精彩评论