threads / discuss / 4138

The git newbie experience

Subject: The git newbie experience

## tl;dr

12 messages between May 14, 2006 and May 15, 2006.

replies: 11people: 5as markdown or json

Tommi Virtanen· May 14, 2006, 18:36 UTC · lore
Here's my thoughts from teaching half a dozen people git recently:

Minimal newbie command set --------------------------

The complete set of "newbie commands" for useful development work should be as small as possible, for fast learning.

People can always look up new things when they want to, but if they don't get the simple things going quickly they will forever see git as "that overcomplex thing I tried to use once".

Concretely: explain the indexless "git commit -a" case first,
so people don't need update-index right away. Most new docs are
pretty good at this, already.

Fix the cases where "git commit -a" is not enough. Here's a case I ran into:

- Jack is a beginning user of git and does not (want to) understand
  the index (right now).
- Jack works on branch X, say his HEAD points to X1. He has an edited,
  uncommitted files with the names A, B and C.
- Jack wants to pull new changes made by others to his branch.
  There are merge conflicts in files D, E, ..., Z.
- Jack resolves the merge conflicts and is ready to commit the resulting
  merge. Note files A, B and C should not be committed.
  1. if Jack says "git commit -a", then A, B and C will be committed
     also.
  2. if Jack says "git commit", then the current state of the index will
     be committed. That is, the commit will not contain the proper
     merged state of files D, E, ..., Z.
  3. if Jack says "git commit D E ... Z", things work correctly. But
     Jack does not want to type or copy-paste that much, and that's
     horribly error-prone anyway. If the leaves one file out, or
     accidentally adds B there too, the merge goes wrong.
  4. if Jack says "git update-index --refresh" and then "git commit",
     things work correctly. But Jack doesn't (want to) know about the
     index.

My best idea so far is to add a "git commit -A" option, that essentially does the "update-index --refresh". Whenever index has a file state != HEAD, update-index it. The modified unrelated files will have index state == HEAD. Or altering "git commit -a" to do that.

Except, trying to solve usability problems by _adding_ options is just insane.

I'd really like to see a way to use git without caring about the index, and just having things work. I can appreciate the index is useful, and possibly even necessary to work on projects the size of the Linux kernel, but I really wish it would default to being _only_ an optimization, not the central bit of user interface to prepare commits.

Basic git use _should_ be as easy as basic svn/bzr/hg use. Anything else will just mean git is not used outside of what codebases Linus has dictatorship over. (At least not after bzr is more done or hg more well known; right now git has a very nice feature set, but the others are catching up fast.)

This is a part that Subversion got right. Basic use needs to be simple. (If you catch me in agitated mood, I'd claim it's the _only_ part Subversion got right;-)

And to reply to a comment on IRC I missed at the time:

(21:05:35) gitster: as gittus said much earlier, refusing index is like refusing git. We might be able to implement "index-less" mode in which things like merge and am refuse to operate when you have any change from HEAD in the working tree. Then new users can always do "commit -a". (21:06:04) gitster: "git-repo-config core.newuser yes" perhaps?

If you do it that way, you only make git unnecessarily hard to use for newbies. For example, we had a case where we absolutely _had_ to keep an ugly workaround in the tree, in a file not otherwise edited, but we definitely did not want to commit the kludge, at least not to the branch that was really being worked on. So such restricted mode would just have meant either people could not merge, or they had to use index anyway. That's a point where people who have a choice make on, and stop trying to use git.

(21:08:34) ***gitster personally considers getting more users a very high priority but agrees that from usability point of view, having a mode to expose "stripped down" set of features for simple needs would be beneficial.

That I can 100% agree with.

Branch management -----------------

"master" and "origin" are good enough for the really simple use, but that starts to fail fast when you add in more branches.

The remotes/* branch support is really nice, but should be used better. Here's a bunch of wild ideas:

- When cloning a repository, just clone all the non-remote heads to
local remotes/* heads. See what name the remote HEAD points to, store
that locally also as a refs/heads/master, set local HEAD to it. Note,
origin is gone, and is now called remotes/master.
- Alternatively (and I think I like this more): When cloning a
repository, just clone all the non-remote heads to local remotes/*
heads. See what the remote HEAD points to, store that locally
also as a refs/heads/* head, set local HEAD to it. Note, origin and
master are both gone, and accessible via remotes/X and refs/heads/X
(where X is the name remote HEAD pointed to).
- Currently, when people start tracking a new remote branch, they end
up editing .git/remotes/origin and adding new Pull: lines. If they
intend to work on the branch, they also clone the branch locally, and
add a Push: line. Make this simpler. Here's a rough sketch:
  "git track [--read-only] REMOTE [BRANCH | --all]"
  Without --all, git track would:
  - abort if refs/remotes/foo exists
  - add "Pull: foo:remotes/foo" to .git/remotes/origin (or the
    equivalent config file)
  - if --read-only is not given:
    - add "Push: foo:foo" to .git/remotes/origin
  - fetch foo from origin to remotes/foo
  - if --read-only is not given:
    - run "git branch foo remotes/foo"
  With --all, it would do the same, but for everything the REMOTE has
  in refs/remotes/. Exactly when to abort is a bit harder to define,
  but still..
- Or, if that's too much, at least make peek-remote understand
.git/remotes/* shortcuts, so finding out what branches exist is a bit
simpler.
Junio C Hamano· May 14, 2006, 21:26 UTC · re: Tommi Virtanen · lore

Re: The git newbie experience

Tommi Virtanen <tv@inoi.fi> writes:
Show 6 quoted lines
> (21:08:34) ***gitster personally considers getting more users a very
> high priority but agrees that from usability point of view, having a
> mode to expose "stripped down" set of features for simple needs would be
> beneficial.
>
> That I can 100% agree with.

Sorry, I personally consider it is not a very high priority; the above is a typo (otherwise "but agrees" does not make _any_ sense).

The rest of the message I'll find a time to comment on separately, but I am in the middle of migrating the development environment, so it might take some time.

Oh, and please send patches cc'ed to the list at least for a few days, because I am fighting with fetchmail/getmail configuration right now and might lose a few mails while doing so.

Junio C Hamano· May 14, 2006, 22:09 UTC · re: Tommi Virtanen · lore

Re: The git newbie experience

Tommi Virtanen <tv@inoi.fi> writes:
Show 5 quoted lines
> My best idea so far is to add a "git commit -A" option, that
> essentially does the "update-index --refresh". Whenever index
> has a file state != HEAD, update-index it. The modified unrelated
> files will have index state == HEAD. Or altering "git commit -a"
> to do that.

I am not sure what you are trying to achieve by --refresh. It does not update the object names in the index. Maybe you are thinking about --again, but that is when you did something to the index yourself, so it would not buy you much in the "novice faced with merge" case.

Anyway, I think the time to commit is too late to save somebody who does not understand the index. How would you explain why you sometimes need to use -A and sometimes -a? That is why I suggested to make "git pull" and "git merge" refuse to work if there are local changes for novice users, where the definition of novice is "git commit -a" is the only way to make a commit. We can have [core] novice = yes in .git/config for that.

If somebody does not understand the index, if the merge is prevented because the local change does conflict with it, how would you explain why sometimes you can merge and sometimes you cannot?

But if you insist going that route, I would say we could make "git commit -a" on a merge commit to do a bit more magic.

For example, we could make -a do something special for a merge by looking at the presense of .git/MERGE_HEAD.

	- if "commit -a", and without .git/MERGE_HEAD, just grab
          all the local modifications that are not in index yet,
          and commit it.
	- upon "commit -a", and when .git/MERGE_HEAD exists,
          grab the paths that ls-files -u reports, update-index
          them.  Other automerged paths are already registered
          in the index.
> Except, trying to solve usability problems by _adding_ options
> is just insane.

I am not sure if it is "usability" but additional option to simplify things does not sound right, I'd agree.

Show 6 quoted lines
> For example, we had a case where we absolutely _had_ to keep
> an ugly workaround in the tree, in a file not otherwise
> edited, but we definitely did not want to commit the kludge,
> at least not to the branch that was really being worked on. So
> such restricted mode would just have meant either people could
> not merge, or they had to use index anyway.
Your example is a very ill-thought out one.

If you are leaving the uncommitable kludge around, you cannot be using "commit -a" with the normal non-merge workflow. Why would you worry about not being able to do "commit -a" on a merge then?

For the beginning user without index, I would rewrite your scenario like this.

- Jack is a beginning user of git and does not (want to) understand
  the index (right now).
- Jack works on branch X, say his HEAD points to X1. He has an edited,
  uncommitted files with the names A, B and C.
- Jack wants to pull new changes made by others to his branch.
  But "git merge" invoked from "git pull" says he needs to stash
  away the local changes to do the merge.
- Jack stashes away what he has been working on and cleans up
  his mess.
  git diff >P.diff
  git checkout HEAD A B C
- Jack then pulls.  There are merge conflicts in files D, E, ..., Z.
- Jack resolves the merge conflicts and is ready to commit the resulting
  merge. Note files A, B and C do not have his unfinished work.
  There is no "if Jack does this or that" problem; he says "git
  commit -a" because that is the only "commit" command he knows
  about.
- Jack then reapplies what he stashed away with "git apply P.diff"
  and keeps working.

Maybe "git stash" command that does "git diff --full-index" with some frills, and "git unstash" command which does an equivalent of "git am -3" would help this workflow (bare "git apply" does not do the three-way merge like am does).

Tommi Virtanen· May 15, 2006, 05:06 UTC · re: Junio C Hamano · lore

Re: The git newbie experience

Junio C Hamano wrote:
> Anyway, I think the time to commit is too late to save somebody
> who does not understand the index.  How would you explain why
> you sometimes need to use -A and sometimes -a? 
I guess what I really want is "a smarter -a".
Show 10 quoted lines
> That is why I
> suggested to make "git pull" and "git merge" refuse to work if
> there are local changes for novice users, where the definition
> of novice is "git commit -a" is the only way to make a commit.
> We can have [core] novice = yes in .git/config for that.
> 
> If somebody does not understand the index, if the merge is
> prevented because the local change does conflict with it, how
> would you explain why sometimes you can merge and sometimes you
> cannot?

By the same logic that is already implemented. "pull refuses to pull changes to files that are modified but not committed".

Show 9 quoted lines
>> For example, we had a case where we absolutely _had_ to keep
>> an ugly workaround in the tree, in a file not otherwise
>> edited, but we definitely did not want to commit the kludge,
> Your example is a very ill-thought out one.
>
> If you are leaving the uncommitable kludge around, you cannot be
> using "commit -a" with the normal non-merge workflow.  Why
> would you worry about not being able to do "commit -a" on a
> merge then?

The indexless working mode means you know two kinds of commits. "git commit -a" or "git commit FILE..". The uncommitted kludge hanging around means people listed file names. The case where the merge differs is that it's not just a few files, and they didn't even really know what files to list. And "git status" showed them something they were not used to seeing.

> For the beginning user without index, I would rewrite your
> scenario like this.
> 
...
Show 5 quoted lines
> - Jack stashes away what he has been working on and cleans up
>   his mess.
> 
>   git diff >P.diff
>   git checkout HEAD A B C
...
Show 7 quoted lines
> - Jack then reapplies what he stashed away with "git apply P.diff"
>   and keeps working.
> 
> Maybe "git stash" command that does "git diff --full-index" with
> some frills, and "git unstash" command which does an equivalent
> of "git am -3" would help this workflow (bare "git apply" does
> not do the three-way merge like am does).

Oh, I'd love to have a quick stash, that's what we actually ended up doing a lot. Although I'd rather see a real implementation use a branch and not just a diff file, but.. yes please.

Although, "git stash" and "git unstash" are yet another command to add to the newbie set, and I just complained about the size of the set ;)

-- 
Inoi Oy, Tykistökatu 4 D (4. krs), FI-20520 Turku, Finland
http://www.inoi.fi/
Mobile +358 40 762 5656
Junio C Hamano· May 15, 2006, 05:18 UTC · re: Tommi Virtanen · lore

Re: The git newbie experience

Tommi Virtanen <tv@inoi.fi> writes:
> Oh, I'd love to have a quick stash, that's what we actually ended up
> doing a lot. Although I'd rather see a real implementation use a branch
> and not just a diff file, but.. yes please.

I'd rather do that with a diff file that can be used to do a 3-way (see how rebase does it with --full-index diff with am -3). No point creating and forgetting to remove a throw away branch and getting more complaints.

Shawn Pearce· May 15, 2006, 05:31 UTC · re: Junio C Hamano · lore

Re: The git newbie experience

Junio C Hamano <junkio@cox.net> wrote:
Show 10 quoted lines
> Tommi Virtanen <tv@inoi.fi> writes:
> 
> > Oh, I'd love to have a quick stash, that's what we actually ended up
> > doing a lot. Although I'd rather see a real implementation use a branch
> > and not just a diff file, but.. yes please.
> 
> I'd rather do that with a diff file that can be used to do a
> 3-way (see how rebase does it with --full-index diff with am -3).
> No point creating and forgetting to remove a throw away branch
> and getting more complaints.

How is a quick stash different from a topic branch? I don't see any difference between the two. Your working directory was a topic branch, just an unnamed topic branch. Why don't you name it and deal with it once it is named?

I can see new users getting confused about what changes are in their quick stash or accidentially losing their quick stash by running it twice in a row.

Teaching new users to always work on a topic branch and committing before pulling/merging should be the favored workflow.

-- 
Shawn.
Junio C Hamano· May 15, 2006, 08:39 UTC · re: Shawn Pearce · lore

Re: The git newbie experience

Shawn Pearce <spearce@spearce.org> writes:
Show 6 quoted lines
>> I'd rather do that with a diff file that can be used to do a
>> 3-way (see how rebase does it with --full-index diff with am -3).
>> No point creating and forgetting to remove a throw away branch
>> and getting more complaints.
>
> How is a quick stash different from a topic branch?

The original version of my message in response to TV looked like this.

 - Jack is a beginning user of git and does not (want to) understand
   the index (right now).
 - Jack works on branch X, say his HEAD points to X1. He has an edited,
   uncommitted files with the names A, B and C.
 - Jack wants to pull new changes made by others to his branch.
   But "git merge" invoked from "git pull" says he needs to stash
   away the local changes to do the merge.
 - Jack stashes away what he has been working on and cleans up
   his mess.
   git checkout -b stash ;# risks error when "stash" exists
   git commit -a -m 'Stashing WIP'
   git checkout master ;# assuming that was where he was
 - Jack then pulls.  There are merge conflicts in files D, E, ..., Z.
 - Jack resolves the merge conflicts and is ready to commit the resulting
   merge. Note files A, B and C do not have his unfinished work.
   There is no "if Jack does this or that" problem; he says "git
   commit -a" because that is the only "commit" command he knows
   about.
 - Jack then reapplies what he stashed away, and keeps working.
   git pull . --no-commit stash
   git branch -D stash

You have to teach the new user to (1) name something, only to immediately discard it when he returns to what he was in the middle of, (2) remember to clean up the temporary thing once he is done lest he forgets to clean it up (and common names like "stash", "tmp" will be reused by accident causing grief next time he needs to do another stash), and (3) use of --no-commit pull.

On the other hand, "git stash/unstash" workflow would be quite simple:

	$ git stash >my.precious.state
        ... do whatever you want to deviate to
        $ git unstash <my.precious.state

Merge resolve might be needed while unstashing, but we are talking about pulling somebody else's work in "do whatever" part, so that is something the user knows how to perform anyway.

A quick and dirty stash implementation would go like this:
Stash is easy.
        #!/bin/sh
        # git stash
        git diff --binary HEAD
        git reset --hard
Unstash is a bit involved.
        #!/bin/sh
        # git unstash
        . git-sh-setup
        O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
        O_DIR=`cd "$GIT_DIR" && pwd`
        stash="$O_DIR/.stash$$"
        rm -fr "$stash.*"
        trap 'rm -rf $stash.*' 0
        cat >"$stash.patch"
        git-apply -z --index-info <"$stash.patch" >"$stash.list"
        GIT_INDEX_FILE="$stash.index"  \
        GIT_OBJECT_DIRECTORY="$O_OBJECT" \
        (
                mkdir -p "$stash.tmp" &&
                git-update-index -z --index-info <"$stash.list" &&
                git-write-tree >"$stash.base" &&
                cd "$stash.tmp" &&
                git-apply --binary --index <"$stash.patch" &&
                git-write-tree >"$stash.his"
        )
        his_tree=$(cat "$stash.his")
        orig_tree=$(cat "$stash.base")
        rm -fr "$stash.*"
        git-merge-resolve $orig_tree -- HEAD $his_tree

This is essentially the core of "am -3" logic; if you are going to use this for real, you would probably want to see if the patch applies cleanly before falling back on the three-way merge, though.

Carl Baldwin· May 15, 2006, 16:46 UTC · re: Junio C Hamano · lore

Re: The git newbie experience

Junio,

This seems a lot like what I tried to do with git-undo/git-redo quite a while back.

My implementation actually wrote the working file state into the object store as a tree and stored a reference to the tree under something like .git/refs/undo (or .git/refs/stash). Redo was a simple merge of this tree back onto the current working files.

I think I would like something like this better than the 'generate binary patch and reapply the patch later.

If these commands were added to git I think they would be better if the commands chose a temporary branch name, committed the current state to that branch and did everything else that someone more experienced would do using branches having chosen a temporary branch name.

The redo or unstash operation could just pick the most recent tempory branch name (top of stack) and merge changes into the working copy.

Carl
On Mon, May 15, 2006 at 01:39:08AM -0700, Junio C Hamano wrote:
Show 110 quoted lines
> Shawn Pearce <spearce@spearce.org> writes:
> 
> >> I'd rather do that with a diff file that can be used to do a
> >> 3-way (see how rebase does it with --full-index diff with am -3).
> >> No point creating and forgetting to remove a throw away branch
> >> and getting more complaints.
> >
> > How is a quick stash different from a topic branch?
> 
> The original version of my message in response to TV looked like
> this.
> 
>  - Jack is a beginning user of git and does not (want to) understand
>    the index (right now).
> 
>  - Jack works on branch X, say his HEAD points to X1. He has an edited,
>    uncommitted files with the names A, B and C.
> 
>  - Jack wants to pull new changes made by others to his branch.
>    But "git merge" invoked from "git pull" says he needs to stash
>    away the local changes to do the merge.
> 
>  - Jack stashes away what he has been working on and cleans up
>    his mess.
> 
>    git checkout -b stash ;# risks error when "stash" exists
>    git commit -a -m 'Stashing WIP'
>    git checkout master ;# assuming that was where he was
> 
>  - Jack then pulls.  There are merge conflicts in files D, E, ..., Z.
> 
>  - Jack resolves the merge conflicts and is ready to commit the resulting
>    merge. Note files A, B and C do not have his unfinished work.
> 
>    There is no "if Jack does this or that" problem; he says "git
>    commit -a" because that is the only "commit" command he knows
>    about.
> 
>  - Jack then reapplies what he stashed away, and keeps working.
> 
>    git pull . --no-commit stash
>    git branch -D stash
> 
> You have to teach the new user to (1) name something, only to
> immediately discard it when he returns to what he was in the
> middle of, (2) remember to clean up the temporary thing once he
> is done lest he forgets to clean it up (and common names like
> "stash", "tmp" will be reused by accident causing grief next
> time he needs to do another stash), and (3) use of --no-commit
> pull.
> 
> On the other hand, "git stash/unstash" workflow would be quite
> simple:
> 
> 	$ git stash >my.precious.state
>         ... do whatever you want to deviate to
>         $ git unstash <my.precious.state
> 
> Merge resolve might be needed while unstashing, but 
> we are talking about pulling somebody else's work in "do
> whatever" part, so that is something the user knows how to
> perform anyway.
> 
> A quick and dirty stash implementation would go like this:
> 
> Stash is easy.
> 
>         #!/bin/sh
>         # git stash
>         git diff --binary HEAD
>         git reset --hard
> 
> Unstash is a bit involved.
> 
>         #!/bin/sh
>         # git unstash
>         . git-sh-setup
>         O_OBJECT=`cd "$GIT_OBJECT_DIRECTORY" && pwd`
>         O_DIR=`cd "$GIT_DIR" && pwd`
>         stash="$O_DIR/.stash$$"
>         rm -fr "$stash.*"
>         trap 'rm -rf $stash.*' 0
>         cat >"$stash.patch"
>         git-apply -z --index-info <"$stash.patch" >"$stash.list"
>         GIT_INDEX_FILE="$stash.index"  \
>         GIT_OBJECT_DIRECTORY="$O_OBJECT" \
>         (
>                 mkdir -p "$stash.tmp" &&
>                 git-update-index -z --index-info <"$stash.list" &&
>                 git-write-tree >"$stash.base" &&
>                 cd "$stash.tmp" &&
>                 git-apply --binary --index <"$stash.patch" &&
>                 git-write-tree >"$stash.his"
>         )
>         his_tree=$(cat "$stash.his")
>         orig_tree=$(cat "$stash.base")
>         rm -fr "$stash.*"
>         git-merge-resolve $orig_tree -- HEAD $his_tree
> 
> This is essentially the core of "am -3" logic; if you are going
> to use this for real, you would probably want to see if the
> patch applies cleanly before falling back on the three-way
> merge, though.
> 
> 
> -
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 
-- 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Carl Baldwin                        RADCAD (R&D CAD)
 Hewlett Packard Company
 MS 88                               work: 970 898-1523
 3404 E. Harmony Rd.                 work: Carl.N.Baldwin@hp.com
 Fort Collins, CO 80525              home: Carl@ecBaldwin.net
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Junio C Hamano· May 15, 2006, 20:47 UTC · re: Carl Baldwin · lore

Re: The git newbie experience

Carl Baldwin <cnb@fc.hp.com> writes:
Show 7 quoted lines
> My implementation actually wrote the working file state into the object
> store as a tree and stored a reference to the tree under something like
> .git/refs/undo (or .git/refs/stash).  Redo was a simple merge of this
> tree back onto the current working files.
>
> I think I would like something like this better than the 'generate
> binary patch and reapply the patch later.

When you think of the "binary patch" as a human readable representation of your (hierarchical set of) tree objects, you would realize that these two approaches aren't that much different at the tree merge level, and it's just a matter of which representation is more convenient and human readable.

Pros and cons I see are:
 * Branch approach needs to teach users only one thing -- create
   a branch, merge with it, throw it away.  Which is something
   the user needs to know anyway, so it is a plus.
 * Branch approach needs to store a full postimage tree and the
   base commit (so you can use it as a merge base); the
   postimage tree includes paths that are not involved in the
   change being stashed.
 * Patch records only the object names of paths that are relevant
   to the stash.  Instead of keeping the full postimage tree, it
   creates one on the fly when you actually do the unstashing.
 * Patch is human readable and can be used for purposes other
   than falling back to a three-way merge.  When cleanly applies
   apply + write-tree is faster than a tree merge.
 * Patch could be verbose if the change being stashed is large;
   after all the primary information used are the object names
   recorded on the "index" lines and the patch text itself is a
   waste from storage point of view.  This is a disadvantage of
   the "patch" approach, but its readability might offset it.
   If a change being stashed is large, the user had better be
   doing it on a separate topic branch anyway, so this might not
   be a big issue.
Carl Worth· May 15, 2006, 20:42 UTC · re: Junio C Hamano · lore

Re: The git newbie experience

On Mon, 15 May 2006 01:39:08 -0700, Junio C Hamano wrote:
Show 6 quoted lines
>  - Jack stashes away what he has been working on and cleans up
>    his mess.
> 
>    git checkout -b stash ;# risks error when "stash" exists
>    git commit -a -m 'Stashing WIP'
>    git checkout master ;# assuming that was where he was
I really like the proposal made elsewhere to implement a new:
	commit -b <newbranch>

which would then allow for a single command to achieve at least the first two commands above:

	git commit -a -b stash -m 'Stashing WIP'

It might even make sense for this command to effectively perform all three of the above commands. That is, should "commit -b" also checkout the newly created branch or should it leave HEAD unchanged. I'm not sure.

Show 7 quoted lines
> You have to teach the new user to (1) name something, only to
> immediately discard it when he returns to what he was in the
> middle of, (2) remember to clean up the temporary thing once he
> is done lest he forgets to clean it up (and common names like
> "stash", "tmp" will be reused by accident causing grief next
> time he needs to do another stash), and (3) use of --no-commit
> pull.

I threw out a simple git-stash earlier, (which stashed to a branch rather than to a file). I've spent some time using it, and am now quite sure it's the wrong thing, and the above problems outline the defect quite well:

1) Naming.
   Here, git-stash is doing too much. I prefer the idea of a stash
   command using a branch rather than a patch file, (and allowing one
   stash per branch rather than one stash per repository). But the
   namespace of branches is something the user owns, and we should
   avoid adding commands that steal from it unnecessarily. So my
   git-stash fails on this point, while "commit -b <newbranch>" is
   much better.
2) Cleanup and --no-commit pull
   Here, git-stash is doing too little. It's really only performing
   one piece of what needs to be done in order to switch back and
   forth between different topics of work.
So here are my thoughts on what I'd like instead:
In git, a branch is what we use to name a topic of work.

Historically, a branch has been extremely lightweight, (a name and reference to a parent for subsequent commits). But there's been a recent trend (in proposals at least) to add other, useful things to a branch, (as in the discussions of branch-specific configuration).

In my work, I've found that the uncommitted state of my working tree is something that I associate very strongly with my "current topic" and expect the branch-changing commands respected that.

In particular, when using checkout to change branches, unless I've specifically stated with "-m" that I want to carry my changes along, I would like git to stash my working tree "into" the branch I'm switching away from.

Similarly, when switching to a branch, I'd like to have the working tree restored to what it was the last time I switched away from that branch.

Does that seem unreasonable to anyone?

The only snag I've imagined is that when using "checkout -m" to switch to a branch that also had a stashed working tree, then there's a merge to be performed and that could obviously conflict. I've intentionally not mentioned how the stashing/restoring should be implemented, since the user shouldn't care. But a merge conflict is one case where the implementation might leak out to the user. The wimpy thing to do would be to refuse to allow "checkout -m" to a branch with stashed changes.

-Carl
Junio C Hamano· May 15, 2006, 21:10 UTC · re: Carl Worth · lore

Re: The git newbie experience

Carl Worth <cworth@cworth.org> writes:
Show 10 quoted lines
> In particular, when using checkout to change branches, unless I've
> specifically stated with "-m" that I want to carry my changes along, I
> would like git to stash my working tree "into" the branch I'm
> switching away from.
>
> Similarly, when switching to a branch, I'd like to have the working
> tree restored to what it was the last time I switched away from that
> branch.
>
> Does that seem unreasonable to anyone?

I would not call it unreasonable to want to have an easy access to that mode of operation _as_ _well_, as an option.

If you were suggesting to make it the only way for future git to work (I think you are not), then it sounds very unreasonable to me.

The implementation behind the scene does not matter, but I think set of "stashes" that can be attached to each branch would work well for what you would want. OTOH, isn't it called stgit?

The reason why I want it to stay as an option is because I often do a throw-away patch on top of whatever branch is checked out in my working tree while reading the list traffic to compose a response with an alternative patch. Usually I follow that by a "git reset" once the message is sent out, but when I like the idea well enough, I do "checkout -b jc/that-topic master" to switch to a new branch with the dirty state carried along, to work on it further. I do not want that workflow to require -m flag, which means something different (-m means "I accept the possibility of the three-way merge failing while doing this switch that needs a merge"). Instead, I want "checkout -b" to try carrying state forward and stop if it cannot without file-level merging (i.e. the current behaviour). Then I can think if I want to do a stash with "git diff", or if I want to do the temporary branch not based on "master" but the current branch (which is guaranteed to work without -m) and deal with the mess later.

Shawn Pearce· May 15, 2006, 05:27 UTC · re: Tommi Virtanen · lore

Re: The git newbie experience

Tommi Virtanen <tv@inoi.fi> wrote:
> Junio C Hamano wrote:
[snip]
Show 20 quoted lines
> > - Jack stashes away what he has been working on and cleans up
> >   his mess.
> > 
> >   git diff >P.diff
> >   git checkout HEAD A B C
> ...
> > - Jack then reapplies what he stashed away with "git apply P.diff"
> >   and keeps working.
> > 
> > Maybe "git stash" command that does "git diff --full-index" with
> > some frills, and "git unstash" command which does an equivalent
> > of "git am -3" would help this workflow (bare "git apply" does
> > not do the three-way merge like am does).
> 
> Oh, I'd love to have a quick stash, that's what we actually ended up
> doing a lot. Although I'd rather see a real implementation use a branch
> and not just a diff file, but.. yes please.
> 
> Although, "git stash" and "git unstash" are yet another command to add
> to the newbie set, and I just complained about the size of the set ;)

This is perhaps one area where SVN's user interface is actually nice. SVN's equiv. of stash is making a copy of your working directory into the repository; something that is rather simple to do for the user.

What about "git commit -b foo -a" to commit the current working directory to branch 'foo'?

Then restoring is a pull of foo ("git pull . foo"), but that intermediate commit is now part of the repository history. And "git commit -a" doesn't automatically add extra/other files to the repository and it probably should in the case of a "stash".

-- 
Shawn.

← back to recent threads