Skip to contentSkip to conversation

question concerning branches

DISCUSSION Ingo Brueckl 12 participants 21 messages Jun 15, 2016

lore
Share & tools
lore
#0 lore raw

I'm a git novice and have a comprehension question concerning branches.

Within a git repository, I do:

git branch test
git checkout test

  # edit foo.bar
  git checkout master

I'd expect that master is in the exactly same unchanged state it was at branching time, but what a surprise, foo.bar is modified here, too!

If I continue now working in the master branch (applying patches and such) I will use a changed foo.bar with testing branch content. I can't even apply patches to foo.bar without conflict.

Of what use are branches if the files aren't totally separated from each other?

What must I do to get a test branch I can't work without affecting master?

Ingo

#1 lore raw
Quoted 9 lines · Ingo Brueckl

Within a git repository, I do:

 git branch test
 git checkout test

 # edit foo.bar
 git checkout master

I'd expect that master is in the exactly same unchanged state it was at branching time, but what a surprise, foo.bar is modified here, too!

You seem to have forgotten the "git commit" step before switching back to master. You have a modified file in your repository; what did you *want* to happen when you switched branches? (Many people find the current behaviour very convenient.)

You might also want to look at the "git stash" command.

Avery

#2 lore raw
Quoted 2 lines · Avery Pennarun

You seem to have forgotten the "git commit" step before switching back to master.

No, I passed over the commit in my example. I know that after the commit the things are as they ought to be, but what if I can't do a commit because I am in the middle of coding and have to have a break?

Quoted 2 lines

You have a modified file in your repository; what did you *want* to happen when you switched branches?

I want an unchanged file in master if I switch there (because I worked in a different branch) and a changed version in the test branch.

Why is the *master* different depending on whether my work in test in still going on or committed?!

Actually, I cannot image how branches are practicable if I always have to have in mind possibly still uncommitted work. Shouldn't it be git's work to ensure that master will remain it was when branching?

Without git I'd make a copy for testing new features. With git, it seems that I have to do the same (a clone). This is what I don't understand.

Quoted 1 line

(Many people find the current behaviour very convenient.)

I find it highly confusing. I understood a branch as something I can do in whatever I want without affecting master. But now a learn that everything I do in the branch will happen in master, too, until I commit. Strange. Very strange.

Quoted 1 line

You might also want to look at the "git stash" command.

Yes, but isn't it annoying to leave the test branch always either with stash or commit in order to have an unchanged master?!

Ingo

#3 lore raw
Quoted 8 lines · [email protected] (Ingo Brueckl) writes:
Quoted 2 lines · Avery Pennarun

You seem to have forgotten the "git commit" step before switching back to master.

No, I passed over the commit in my example. I know that after the commit the things are as they ought to be, but what if I can't do a commit because I am in the middle of coding and have to have a break?

Then you use git-stash. It was invented for that.

Quoted 8 lines
Quoted 2 lines

You have a modified file in your repository; what did you *want* to happen when you switched branches?

I want an unchanged file in master if I switch there (because I worked in a different branch) and a changed version in the test branch.

Why is the *master* different depending on whether my work in test in still going on or committed?!

Branches are about commits. State of a working directory doesn't belong to a branch (in Git). Learning concepts behind Git would help you in understanding it (Git is very consistent), which in turn would help in using it.

What about untracked files? Do you want to lose them when you switch branches?

Quoted 7 lines

Actually, I cannot image how branches are practicable if I always have to have in mind possibly still uncommitted work. Shouldn't it be git's work to ensure that master will remain it was when branching?

Without git I'd make a copy for testing new features. With git, it seems that I have to do the same (a clone). This is what I don't understand.

You finish old work (or stash it away), _then_ you begin new work.

Quoted 2 lines
Quoted 2 lines

(Many people find the current behaviour very convenient.)

Take the following example. You started coding some feature on 'master' branch, then you realized that this feature is more complicated than you thought at first, so it should be developed in separate topic branch. You do "git checkout -b featureA", and voila you are now coding on feature branch 'featureA'.

Quoted 4 lines
Quoted 1 line

You might also want to look at the "git stash" command.

Yes, but isn't it annoying to leave the test branch always either with stash or commit in order to have an unchanged master?!

No, it isn't.

Signature · 3 lines
Jakub Narebski

Git User's Survey 2009: http://tinyurl.com/GitSurvey2009
#4 lore raw
Quoted 1 line · Jakub Narebski

You finish old work (or stash it away), _then_ you begin new work.

Ok, this helps me a little bit to understand.

The branches aren't designed to split my work, but rather something to collect the different parts of my work.

But as software development often is something where you are coding on several issues at the same time which can't be committed immediately, it sounds that 'stash' is the developer's best friend.

Ingo

#5 lore raw
Quoted 4 lines · Ingo Brueckl
Quoted 1 line · Jakub Narebski

You finish old work (or stash it away), _then_ you begin new work.

Quoted 8 lines

Ok, this helps me a little bit to understand.

The branches aren't designed to split my work, but rather something to collect the different parts of my work.

But as software development often is something where you are coding on several issues at the same time which can't be committed immediately, it sounds that 'stash' is the developer's best friend.

Context switching has overhead; so it's usually better to try to complete one task before switching to another. Granted, sometimes it can't be done, but it's something you should really try to do.

Also, commits are easier to review if they are kept small; if you localize changes into separate commits, it's often easier to detet problems when doing "git bisect", for example. So if you are often needing to switch while leaving something that isn't ready to be committed, you might want to ask yourself if you are putting too many changes into a single ocmmit.

Personally, in the cases where I can't finish a commit before I need to switch away to another branch, my preference is to not use "git stash", but instead to create a topic branch, and then check in a partially completed change on the topic branch, which I can later ammend using "git commit --amend" (or if I have multiple commits on the topic branch, "git rebase --interactive"). This is because I can use the commit description to leave myself some notes about what still needs to be done before the commit can be finalized.

- Ted

#6 lore raw
Quoted 8 lines · Theodore Tso

Personally, in the cases where I can't finish a commit before I need to switch away to another branch, my preference is to not use "git stash", but instead to create a topic branch, and then check in a partially completed change on the topic branch, which I can later ammend using "git commit --amend" (or if I have multiple commits on the topic branch, "git rebase --interactive"). This is because I can use the commit description to leave myself some notes about what still needs to be done before the commit can be finalized.

Errr... you are aware that you can use "git stash save <message>" (i.e. specify commit message for stash; well, the subject), don't you?

Signature · 2 lines
Jakub Narebski
Poland
#7 lore raw
Quoted 2 lines · Jakub Narebski

Errr... you are aware that you can use "git stash save <message>" (i.e. specify commit message for stash; well, the subject), don't you?

I wasn't aware, but usually I like leaving more notes to myself than just a single lines' worth of state. I should probably take another look at "git stash" and see if it's a handy tool for me to use; but so far I've been happy enough with "git checkout -b topic-name; git commit".

- Ted

#8 lore raw
In reply to Ingo Brueckl
Quoted 8 lines · Ingo Brueckl
Quoted 1 line · Jakub Narebski

You finish old work (or stash it away), _then_ you begin new work.

Ok, this helps me a little bit to understand.

The branches aren't designed to split my work, but rather something to collect the different parts of my work.

Well, git is flexible enough that it can support also the workflow you tried to use.

Namely you can have many working directories tied to single repository (each of those checkouts should be of different branch). You can use git-new-workdir script from contrib/worktree for that. Then to switch branches you would just cd to appropriate directory (and keep unsaved changes and untracked files). That said it is [much] less used workflow.

Quoted 3 lines

But as software development often is something where you are coding on several issues at the same time which can't be committed immediately, it sounds that 'stash' is the developer's best friend.

Well, you can also commit and then clean up history with interactive rebase (or patch management interface such as StGit or Guilt). In distributed version control systems like Git the act of publishing changes is separate from the act of committing them (you should not rewrite published history, though).

Signature · 2 lines
Jakub Narebski
Poland
#9 lore raw
In reply to Ingo Brueckl
Quoted 4 lines · Ingo Brueckl
Quoted 1 line · Jakub Narebski

You finish old work (or stash it away), _then_ you begin new work.

Quoted 8 lines

Ok, this helps me a little bit to understand.

The branches aren't designed to split my work, but rather something to collect the different parts of my work.

But as software development often is something where you are coding on several issues at the same time which can't be committed immediately, it sounds that 'stash' is the developer's best friend.

Or you could just 'commit' more frequently, but don't 'push' so you're not disturbing anyone else until you're done.

This is a big difference from how centralized VCSs work: there, a commit is a major operation that you're afraid to do in case you make someone else mad. In git, commits are cheap, you just need to be careful about pushing.

(You can also clean up your series of commits before pushing by using 'git rebase')

Have fun,

Avery

#10 lore raw
Quoted 4 lines · Avery Pennarun

This is a big difference from how centralized VCSs work: there, a commit is a major operation that you're afraid to do in case you make someone else mad. In git, commits are cheap, you just need to be careful about pushing.

I second this. "commit" is indeed a bad name in decentralized VCSs. There's no commitment from the developer, you don't commit to swear the code is good, but you commit just to record a set of changes, and attach a descriptive message to it.

In Git, a commit takes usually a fraction of a second, and can be modified later easily until it's pushed.

'git commit --amend' will allow you to change either the changes or the message associated to a commit.

'git reset HEAD^' will just undo the commit, turning your commited changes into uncommited ones.

'git rebase --interactive' will allow you another bunch of cool things.

So, keeping your changes uncommited has very few advantage, and indeed has one big drawback: uncommited changes have no descriptive message associated, so if you leave a branch for some time, it makes it more difficult for you to remember what you were doing on it when you switch back to it:

git checkout -b topic-branch

# edit foo.bar
git checkout master
# hack
git commit
# go on holiday
# come back
git checkout topic-branch
git status
# err, what is this all about?

OTOH:

git checkout -b topic-branch

# edit foo.bar
git commit -m "started feature foo, but bar is still to be done"
git checkout master
# hack
git commit
# go on holiday
# come back
git checkout topic-branch
git show
# Ah, I remember!

Actually, all the VCSs I know about (Mercurial, Bazaar, Subversion, and IIRC GNU Arch) deal with this the way Git does. Hey! there must be a reason ;-).

Signature · 1 line
Matthieu
#11 lore raw
In reply to Ingo Brueckl
Quoted 7 lines · Ingo Brueckl

But as software development often is something where you are coding on several issues at the same time which can't be committed immediately, it sounds that 'stash' is the developer's best friend.

Ingo

There is no problem with having temporary commits on local branches, however.

Quite frequently, I'll "git commit -a -m 'Temp commit'; git checkout other-branch". As long as you don't make these temporary commits public, it's very easy to munge them (See: "git rebase --interactive <commitish>", and "git reset --soft <commitish>").

-Jacob

#12 lore raw
In reply to Ingo Brueckl
Quoted 8 lines · Ingo Brueckl
Quoted 1 line · Jakub Narebski

You finish old work (or stash it away), _then_ you begin new work.

Ok, this helps me a little bit to understand.

The branches aren't designed to split my work, but rather something to collect the different parts of my work.

Hmm. Yes. That's one way of looking at it.

At the same time, thinking about it another way may explain the git choices in this area. There's two issues:

- if we _don't_ carry the edits around across branch switches, then what would we do?

Basically, since you haven't committed things, it's kind of floating around. You switch to another branch, what should we do? There are really only two choices: either we'd need to 'stash' the state with the branch we switch away from (which is apparently what you expected), or we need to just move the changes to the new branch (which is what git does, or complains if it cannot).

Now, 'stashing' the changes is actually very much against the whole git philosophy. Git was built up around the index and the database, and branches have always been pointers to the top-of-commit, so there literally isn't any way to stash things that makes sense. Sure, later on we ended up having the 'stash' command, but that's totally separate from branches, and is an independently useful thing.

- One of the big reasons to act like git does is that the way at least _I_ work is to actually create a new branch with the explicit intention of committing work I have already done!

IOW, your example was

git branch test
git checkout test
# edit foo.bar
git checkout master

and you were surprised that the edit followed you back to the "master" branch, but what is actualyl a much more natural way of working is

	# edit foo.bar
	# realize that this was actually the start of a new feature
	git branch new-feature
	git checkout new-feature
	# maybe continue to edit foo.bar until it's all good
	git commit -a

ie the git behavior explicitly _encourages_ you to not have to decide before-the-fact to create a branch - it may be that only after you've done the changes do you realize that "oops, these changes were _way_ more intrusive than I originally anticipated, and I don't want to commit them on the master branch, I want to commit them on an experimental topic branch instead"

So there are two different reasons why git works the way it does: a pure implementation reason ("working any other way would not fit the git model") and a practical workflow reason ("you are _expected_ to move dirty state around with your branches, because one common case is to create a branch _for_ that dirty state").

Linus

#13 lore raw
Quoted 1 line · Linus Torvalds

branches have always been pointers to the top-of-commit

Obviously I expected them to be pointers on trees.

A kind of automatical starting commit in a newly created branch would at least warn if one has begun changing files and wants to checkout back. (Is this a feature worth of discussion?)

Quoted 2 lines

the git behavior explicitly _encourages_ you to not have to decide before-the-fact to create a branch

Thanks for the explanation which help me to understand why git works like it does.

I'm able to follow your examples, but what I had it mind when I started the topic and my example was:

Assume a project is released (i.e. no more open bugs we know about) - I know we're drifting towards fantasy now. ;-)

On the one hand, I want to add single new features (such as other developers do) which will be written, tested and committed. I want to push/pull frequently to be up to date all the time. (master branch)

On the other hand, I want to completely rewrite the core of the program. (test or rewrite branch)

What is the git way to do this in a the right (and clever) manner?

In a branch, I learned, I have to commit or stash before I return to master for push/pull to follow the project. If I forget, I'm screwed, because files have changed due to the rewrite (in that branch), I won't get a warning until my first commit (in that branch) and commits (in master) will conflict.

Ingo

#14 lore raw

[Please don't cull the Cc list.]

Quoted 3 lines · Ingo Brueckl

On the one hand, I want to add single new features (such as other developers do) which will be written, tested and committed. I want to push/pull frequently to be up to date all the time. (master branch)

If you want to stay up-to-date while you are creating a new feature, then your workflow is sub-optimal. This message by Linus is good to read:

http://www.mail-archive.com/[email protected]/msg39091.html

The important rule is "Don't merge upstream code at random points".

Once you learnt this rule, then you also see that it is not important to always stay up-to-date. It is important to complete (test, debug) your feature on a stable base. During that time, you don't care about upstream; you care about your feature.

Quoted 4 lines

In a branch, I learned, I have to commit or stash before I return to master for push/pull to follow the project. If I forget, I'm screwed, because files have changed due to the rewrite (in that branch), I won't get a warning until my first commit (in that branch) and commits (in master) will conflict.

You are obviously of a CVS or SVN mindset, where making a commit is such an important operation that you don't dare to make it until your work is *completed*.

With a git mindset, it won't happen that you "forget" whether you have anything uncommitted; you simply never have because committing half-baked stuff is the rule, not the exception. That is, before you get a cup of coffee, you commit; before you answer a phone call, you commit; before you turn your attention away, you commit. (That may be exaggerated, perhaps it even isn't, but you get the point.)

When you have completed your work, you go back to make your commit history look nice, comprehensible, and bisectable.

And only then comes the heavy operation: You publish your work for consumption by interested parties. This may be even only you yourself:
"Consumption" would be to merge the work into your release branch. This is the right time to care about upstream again.

-- Hannes

#15 lore raw
Quoted 6 lines · Johannes Sixt
Quoted 4 lines · Ingo Brueckl

In a branch, I learned, I have to commit or stash before I return to master for push/pull to follow the project. If I forget, I'm screwed, because files have changed due to the rewrite (in that branch), I won't get a warning until my first commit (in that branch) and commits (in master) will conflict.

Errr... if having unknown files in status info when comitting doesn't clue you in that you have spurious uncomitted changes,

# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: somefile

and neither commit diff summary

n files changed, kk insertions(+), ll deletions(-)

doesn't clue you in, then you have more serous problems!

Second, you can use git-aware prompt to tell you if you have uncomitted changes, so you will know when switching branches that you have some changes that don't belong to branch you switch from.

Quoted 12 lines

You are obviously of a CVS or SVN mindset, where making a commit is such an important operation that you don't dare to make it until your work is *completed*.

With a git mindset, it won't happen that you "forget" whether you have anything uncommitted; you simply never have because committing half-baked stuff is the rule, not the exception. That is, before you get a cup of coffee, you commit; before you answer a phone call, you commit; before you turn your attention away, you commit. (That may be exaggerated, perhaps it even isn't, but you get the point.)

Quoted 2 lines

When you have completed your work, you go back to make your commit history look nice, comprehensible, and bisectable.

...with "git rebase --interactive" or patch management interface (StGit, Guilt), or topic branch management interface (TopGit).

Quoted 5 lines

And only then comes the heavy operation: You publish your work for consumption by interested parties. This may be even only you yourself:
"Consumption" would be to merge the work into your release branch. This is the right time to care about upstream again.

Signature · 3 lines
Jakub Narebski
Poland
ShadeHawk on #git
#16 lore raw
In reply to Linus Torvalds
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line
Quoted 1 line

"Linus" == Linus Torvalds <[email protected]> writes:

Linus> git branch new-feature
Linus> git checkout new-feature

Or my favorite: "git checkout -b new-feature".

Signature · 4 lines
Randal L. Schwartz - Stonehenge Consulting Services, Inc. - +1 503 777 0095
<[email protected]> <URL:http://www.stonehenge.com/merlyn/>
Smalltalk/Perl/Unix consulting, Technical writing, Comedy, etc. etc.
See http://methodsandmessages.vox.com/ for Smalltalk and Seaside discussion
#17 lore raw
In reply to Ingo Brueckl
Quoted 11 lines · [email protected] (Ingo Brueckl) writes:

I'm a git novice and have a comprehension question concerning branches.

Within a git repository, I do:

git branch test
git checkout test

  # edit foo.bar
  git checkout master

I'd expect that master is in the exactly same unchanged state it was at branching time, but what a surprise, foo.bar is modified here, too!

No. Local modification that are not committed do not belong to any branch. Rather it belongs to your work tree and the index, and follow you when you switch branches.

This is one of the most useful features. For example, it is an essential part of supporting the workflow described here:

    http://gitster.livejournal.com/25892.html

On the other hand, there are cases that you do not want to see your local changes to foo.bar appear when you switch back to master, and the most obvious case is "I started working on something, and I used a separate branch 'test' because the change will be involved. I am in the middle of the work, the changes so far I made to foo.bar is far from ready, but I have to handle emergency fix on the master branch". For that kind of situation, you can use "git stash", like:

    http://gitster.livejournal.com/29577.html
#18 lore raw
Quoted 1 line · Junio C Hamano

This is one of the most useful features.

Wow. I'm sursprised to hear that, because I consider it at the moment as a very strange one.

Quoted 3 lines

For example, it is an essential
part of supporting the workflow described here:

    http://gitster.livejournal.com/25892.html

Here is what I'd expect to do with git (described with my own words, not in git commands):

1. commit the quick fix to the release branch
2. push this single commit to origin and master

Now that all branches have the commit a later push and pull should notice this and "skip" it.

This leads to a second question I have. Assuming I have three patches in my repo (#1, #2 and #3), is it possible to push only #2 (because it is a quick fix) and later, maybe after I committed #4, the rest, i.e. #1, #2 and #4?

Ingo

#19 lore raw
Quoted 17 lines · Ingo Brueckl
Quoted 1 line · Junio C Hamano

This is one of the most useful features.

Wow. I'm sursprised to hear that, because I consider it at the moment as a very strange one.

Quoted 3 lines

For example, it is an essential
part of supporting the workflow described here:

    http://gitster.livejournal.com/25892.html

Here is what I'd expect to do with git (described with my own words, not in git commands):

1. commit the quick fix to the release branch
2. push this single commit to origin and master

Quoted 8 lines

Now that all branches have the commit a later push and pull should notice this and "skip" it.

This leads to a second question I have. Assuming I have three patches in my repo (#1, #2 and #3), is it possible to push only #2 (because it is a quick fix) and later, maybe after I committed #4, the rest, i.e. #1, #2 and #4?

If they're on different branches, yes. If they're on the same branch, no. This is because a commit in git is named uniquely not only by its contents, but also by its ancestry.

You can, however, run "git rebase --interactive" and re-order the commits before you push them, so that #2 becomes #1 and vice versa. Then you can push only #1 (the old #2) while leaving #2 (the old #1), #3 and #4 on your machine only. This involves knowing the commit identifier of #1 though. Assuming it's "deadbeef", you can update the remote branch "foo" to hold your new commit by running the following command:

git push <remote> deadbeef:refs/heads/foo

HTH

Signature · 7 lines
Andreas Ericsson                   [email protected]
OP5 AB                             www.op5.se
Tel: +46 8-230225                  Fax: +46 8-230231

Considering the successes of the wars on alcohol, poverty, drugs and
terror, I think we should give some serious thought to declaring war
on peace.
#20 lore raw
In reply to Ingo Brueckl
Quoted 11 lines · [email protected] (Ingo Brueckl) writes:

I'm a git novice and have a comprehension question concerning branches.

Within a git repository, I do:

git branch test
git checkout test

  # edit foo.bar
  git checkout master

I'd expect that master is in the exactly same unchanged state it was at branching time, but what a surprise, foo.bar is modified here, too!

You didn't commit your change to foo.bar.

[...]

Quoted 1 line

What must I do to get a test branch I can't work without affecting master?

commit changes.