4

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 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    

#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.

Zoe
  • 27,060
  • 21
  • 118
  • 148
MageSlayer
  • 421
  • 4
  • 12

3 Answers3

1

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.

Jakob Borg
  • 23,685
  • 6
  • 47
  • 47
  • Sure I can do that manually, but it requires a lot of work in reality. I'm afraid git bisect won't help me here too. – MageSlayer Mar 14 '10 at 19:06
  • But this does what you want, just call the branch `patches` after the merge. If not, I'm unsure what it is you want to achieve? There is no point in the development you describe that contains files 1 to 4, so you need to create such a point. I don't think any other VCS would be able to checkout a commit that doesn't exist... – Jakob Borg Mar 14 '10 at 19:12
  • Basically I'm trying to present that checkout as linear set of patches to be able to bisect that branch. Your solution is perfectly valid, except that I need manually find out commits refs to merge in two branches. – MageSlayer Mar 14 '10 at 19:21
  • I see. Then I'd say the problem is that the history isn't linear, therefore there isn't a linear set of patches to bisect. That is a disadvantage of working with branches in this way. – Jakob Borg Mar 14 '10 at 19:35
  • Yes. I feel some hackery needed :) – MageSlayer Mar 14 '10 at 19:43
  • Possibly implementing something like http://nvie.com/git-model for the future can help with this. Then you can at least track down which feature branch is guilty, and from that perhaps do bisections within that branch. – Jakob Borg Mar 14 '10 at 19:45
1

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.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • Pretty neat. It looks like a black magic. I'm just trying to understand why commits moved on top of patches instead of tmp, as --onto tmp suggested. According to manual, git rebase [--onto ] [], newbase is tmp! Do I miss something? – MageSlayer Mar 15 '10 at 18:23
  • @mageslayer: commits didn't move on top of `patches`, but on top of `tmp`. `newbase` is indeed `tmp`. `patches` ends up at the right commit simply because, before the `rebase --onto`, I reset it at the right destination place I wanted it to be at. – VonC Mar 15 '10 at 18:49
  • @mageslayer: since a "branch" in Git is really just a label to the tip of a graph path, you can feel free to move that label around... except if you did already published that branch (`push`), which would mean a world of hurt for anyone pulling from your (new) branch: everything would be to be merged again. – VonC Mar 15 '10 at 18:52
  • Hm. You mean that commits are on top of tmp after rebase, but tmp tip is still at test2? Does git move patches tip to tmp last commit? Is it so? – MageSlayer Mar 15 '10 at 19:10
  • `tmp` tip is still at `test2` indeed: the new base for `test3` and `test4` is '`tmp`' (`test2`). since `patches` referenced `test4` before the rebase, it still references `test4` after. – VonC Mar 15 '10 at 19:46
0

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.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285