1. 09 10月, 2020 1 次提交
    • D
      checkout: learn to respect checkout.guess · 64f1f58f
      Denton Liu 提交于
      The current behavior of git checkout/switch is that --guess is currently
      enabled by default. However, some users may not wish for this to happen
      automatically. Instead of forcing users to specify --no-guess manually
      each time, teach these commands the checkout.guess configuration
      variable that gives users the option to set a default behavior.
      
      Teach the completion script to recognize the new config variable and
      disable DWIM logic if it is set to false.
      Signed-off-by: NDenton Liu <liu.denton@gmail.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      64f1f58f
  2. 08 1月, 2020 1 次提交
    • A
      checkout: don't revert file on ambiguous tracking branches · fa74180d
      Alexandr Miloslavskiy 提交于
      For easier understanding, here are the existing good scenarios:
      
        1) Have *no* file 'foo', *no* local branch 'foo' and a *single*
           remote branch 'foo'
        2) `git checkout foo` will create local branch foo, see [1]
      
        and
      
        1) Have *a* file 'foo', *no* local branch 'foo' and a *single*
           remote branch 'foo'
        2) `git checkout foo` will complain, see [3]
      
      This patch prevents the following scenario:
      
        1) Have *a* file 'foo', *no* local branch 'foo' and *multiple*
           remote branches 'foo'
        2) `git checkout foo` will successfully... revert contents of
           file `foo`!
      
      That is, adding another remote suddenly changes behavior significantly,
      which is a surprise at best and could go unnoticed by user at worst.
      Please see [3] which gives some real world complaints.
      
      To my understanding, fix in [3] overlooked the case of multiple remotes,
      and the whole behavior of falling back to reverting file was never
      intended:
      
        [1] introduces the unexpected behavior. Before, there was fallback
        from not-a-ref to pathspec. This is reasonable fallback. After, there
        is another fallback from ambiguous-remote to pathspec. I understand
        that it was a copy&paste oversight.
      
        [2] noticed the unexpected behavior but chose to semi-document it
        instead of forbidding, because the goal of the patch series was
        focused on something else.
      
        [3] adds `die()` when there is ambiguity between branch and file. The
        case of multiple tracking branches is seemingly overlooked.
      
      The new behavior: if there is no local branch and multiple remote
      candidates, just die() and don't try reverting file whether it
      exists (prevents surprise) or not (improves error message).
      
      [1] Commit 70c9ac2f ("DWIM "git checkout frotz" to "git checkout -b frotz origin/frotz"" 2009-10-18)
          https://public-inbox.org/git/7vaazpxha4.fsf_-_@alter.siamese.dyndns.org/
      [2] Commit ad8d5104 ("checkout: add advice for ambiguous "checkout <branch>"", 2018-06-05)
          https://public-inbox.org/git/20180502105452.17583-1-avarab@gmail.com/
      [3] Commit be4908f1 ("checkout: disambiguate dwim tracking branches and local files", 2018-11-13)
          https://public-inbox.org/git/20181110120707.25846-1-pclouds@gmail.com/Signed-off-by: NAlexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      fa74180d
  3. 14 11月, 2018 1 次提交
    • N
      checkout: disambiguate dwim tracking branches and local files · be4908f1
      Nguyễn Thái Ngọc Duy 提交于
      When checkout dwim is added in [1], it is restricted to only dwim when
      certain conditions are met and fall back to default checkout behavior
      otherwise. It turns out falling back could be confusing. One of the
      conditions to turn
      
          git checkout frotz
      
      to
      
          git checkout -b frotz origin/frotz
      
      is that frotz must not exist as a file. But when the user comes to
      expect "git checkout frotz" to create the branch "frotz" and there
      happens to be a file named "frotz", git's silently reverting "frotz"
      file content is not helping. This is reported in Git mailing list [2]
      and even used as an example of "Git is bad" elsewhere [3].
      
      We normally try to do the right thing, but when there are multiple
      "right things" to do, it's best to leave it to the user to decide.
      Check this case, ask the user to to disambiguate:
      
      - "git checkout -- foo" will check out path "foo"
      - "git checkout foo --" will dwim and create branch "foo" [4]
      
      For users who do not want dwim, use --no-guess. It's useless in this
      particular case because "git checkout --no-guess foo --" will just
      fail. But it could be used by scripts.
      
      [1] 70c9ac2f (DWIM "git checkout frotz" to "git checkout -b frotz
          origin/frotz" - 2009-10-18)
      [2] https://public-inbox.org/git/CACsJy8B2TVr1g+k+eSQ=pBEO3WN4_LtgLo9gpur8X7Z9GOFL_A@mail.gmail.com/
      [3] https://news.ycombinator.com/item?id=18230655
      [4] a047fafc (checkout: allow dwim for branch creation for "git
          checkout $branch --" - 2013-10-18)
      Signed-off-by: NNguyễn Thái Ngọc Duy <pclouds@gmail.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      be4908f1
  4. 19 8月, 2018 1 次提交
  5. 31 7月, 2018 1 次提交
  6. 12 6月, 2018 3 次提交
    • Æ
      checkout & worktree: introduce checkout.defaultRemote · 8d7b558b
      Ævar Arnfjörð Bjarmason 提交于
      Introduce a checkout.defaultRemote setting which can be used to
      designate a remote to prefer (via checkout.defaultRemote=origin) when
      running e.g. "git checkout master" to mean origin/master, even though
      there's other remotes that have the "master" branch.
      
      I want this because it's very handy to use this workflow to checkout a
      repository and create a topic branch, then get back to a "master" as
      retrieved from upstream:
      
          (
              cd /tmp &&
              rm -rf tbdiff &&
              git clone git@github.com:trast/tbdiff.git &&
              cd tbdiff &&
              git branch -m topic &&
              git checkout master
          )
      
      That will output:
      
          Branch 'master' set up to track remote branch 'master' from 'origin'.
          Switched to a new branch 'master'
      
      But as soon as a new remote is added (e.g. just to inspect something
      from someone else) the DWIMery goes away:
      
          (
              cd /tmp &&
              rm -rf tbdiff &&
              git clone git@github.com:trast/tbdiff.git &&
              cd tbdiff &&
              git branch -m topic &&
              git remote add avar git@github.com:avar/tbdiff.git &&
              git fetch avar &&
              git checkout master
          )
      
      Will output (without the advice output added earlier in this series):
      
          error: pathspec 'master' did not match any file(s) known to git.
      
      The new checkout.defaultRemote config allows me to say that whenever
      that ambiguity comes up I'd like to prefer "origin", and it'll still
      work as though the only remote I had was "origin".
      
      Also adjust the advice.checkoutAmbiguousRemoteBranchName message to
      mention this new config setting to the user, the full output on my
      git.git is now (the last paragraph is new):
      
          $ ./git --exec-path=$PWD checkout master
          error: pathspec 'master' did not match any file(s) known to git.
          hint: 'master' matched more than one remote tracking branch.
          hint: We found 26 remotes with a reference that matched. So we fell back
          hint: on trying to resolve the argument as a path, but failed there too!
          hint:
          hint: If you meant to check out a remote tracking branch on, e.g. 'origin',
          hint: you can do so by fully qualifying the name with the --track option:
          hint:
          hint:     git checkout --track origin/<name>
          hint:
          hint: If you'd like to always have checkouts of an ambiguous <name> prefer
          hint: one remote, e.g. the 'origin' remote, consider setting
          hint: checkout.defaultRemote=origin in your config.
      
      I considered splitting this into checkout.defaultRemote and
      worktree.defaultRemote, but it's probably less confusing to break our
      own rules that anything shared between config should live in core.*
      than have two config settings, and I couldn't come up with a short
      name under core.* that made sense (core.defaultRemoteForCheckout?).
      
      See also 70c9ac2f ("DWIM "git checkout frotz" to "git checkout -b
      frotz origin/frotz"", 2009-10-18) which introduced this DWIM feature
      to begin with, and 4e853331 ("worktree: make add <path> <branch>
      dwim", 2017-11-26) which added it to git-worktree.
      Signed-off-by: NÆvar Arnfjörð Bjarmason <avarab@gmail.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      8d7b558b
    • Æ
      checkout: add advice for ambiguous "checkout <branch>" · ad8d5104
      Ævar Arnfjörð Bjarmason 提交于
      As the "checkout" documentation describes:
      
          If <branch> is not found but there does exist a tracking branch in
          exactly one remote (call it <remote>) with a matching name, treat
          as equivalent to [...] <remote>/<branch.
      
      This is a really useful feature. The problem is that when you add
      another remote (e.g. a fork), git won't find a unique branch name
      anymore, and will instead print this unhelpful message:
      
          $ git checkout master
          error: pathspec 'master' did not match any file(s) known to git
      
      Now it will, on my git.git checkout, print:
      
          $ ./git --exec-path=$PWD checkout master
          error: pathspec 'master' did not match any file(s) known to git.
          hint: 'master' matched more than one remote tracking branch.
          hint: We found 26 remotes with a reference that matched. So we fell back
          hint: on trying to resolve the argument as a path, but failed there too!
          hint:
          hint: If you meant to check out a remote tracking branch on, e.g. 'origin',
          hint: you can do so by fully qualifying the name with the --track option:
          hint:
          hint:     git checkout --track origin/<name>
      
      Note that the "error: pathspec[...]" message is still printed. This is
      because whatever else checkout may have tried earlier, its final
      fallback is to try to resolve the argument as a path. E.g. in this
      case:
      
          $ ./git --exec-path=$PWD checkout master pu
          error: pathspec 'master' did not match any file(s) known to git.
          error: pathspec 'pu' did not match any file(s) known to git.
      
      There we don't print the "hint:" implicitly due to earlier logic
      around the DWIM fallback. That fallback is only used if it looks like
      we have one argument that might be a branch.
      
      I can't think of an intrinsic reason for why we couldn't in some
      future change skip printing the "error: pathspec[...]" error. However,
      to do so we'd need to pass something down to checkout_paths() to make
      it suppress printing an error on its own, and for us to be confident
      that we're not silencing cases where those errors are meaningful.
      
      I don't think that's worth it since determining whether that's the
      case could easily change due to future changes in the checkout logic.
      Signed-off-by: NÆvar Arnfjörð Bjarmason <avarab@gmail.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      ad8d5104
    • Æ
      checkout tests: index should be clean after dwim checkout · c8cbf20c
      Ævar Arnfjörð Bjarmason 提交于
      Assert that whenever there's a DWIM checkout that the index should be
      clean afterwards, in addition to the correct branch being checked-out.
      
      The way the DWIM checkout code in checkout.[ch] works is by looping
      over all remotes, and for each remote trying to find if a given
      reference name only exists on that remote, or if it exists anywhere
      else.
      
      This is done by starting out with a `unique = 1` tracking variable in
      a struct shared by the entire loop, which will get set to `0` if the
      data reference is not unique.
      
      Thus if we find a match we know the dst_oid member of
      tracking_name_data must be correct, since it's associated with the
      only reference on the only remote that could have matched our query.
      
      But if there was ever a mismatch there for some reason we might end up
      with the correct branch checked out, but at the wrong oid, which would
      show whatever the difference between the two staged in the
      index (checkout branch A, stage changes from the state of branch B).
      
      So let's amend the tests (mostly added in) 399e4a1c ("t2024: Add
      tests verifying current DWIM behavior of 'git checkout <branch>'",
      2013-04-21) to always assert that "status" is clean after we run
      "checkout", that's being done with "-uno" because there's going to be
      some untracked files related to the test itself which we don't care
      about.
      
      In all these tests (DWIM or otherwise) we start with a clean index, so
      these tests are asserting that that's still the case after the
      "checkout", failed or otherwise.
      
      Then if we ever run into this sort of regression, either in the
      existing code or with a new feature, we'll know.
      Signed-off-by: NÆvar Arnfjörð Bjarmason <avarab@gmail.com>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      c8cbf20c
  7. 21 9月, 2016 1 次提交
  8. 15 10月, 2014 1 次提交
    • J
      checkout: report upstream correctly even with loosely defined branch.*.merge · 05e73682
      Junio C Hamano 提交于
      When checking out a branch that is set to build on top of another
      branch (often, a remote-tracking branch), "git checkout" reports how
      your work relates to the other branch, e.g.
      
          Your branch is behind 'origin/master', and can be fast-forwarded.
      
      Back when this feature was introduced, this was only done for
      branches that build on remote-tracking branches, but 5e6e2b48 (Make
      local branches behave like remote branches when --tracked,
      2009-04-01) added support to give the same report for branches that
      build on other local branches (i.e. branches whose branch.*.remote
      variables are set to '.').  Unlike the support for the branches
      building on remote-tracking branches, however, this did not take
      into account the fact that branch.*.merge configuration is allowed
      to record a shortened branch name.
      
      When branch.*.merge is set to 'master' (not 'refs/heads/master'),
      i.e. "my branch builds on the local 'master' branch", this caused
      "git checkout" to report:
      
          Your branch is based on 'master', but the upstream is gone.
      
      The upstream is our repository and is definitely not gone, so this
      output is nonsense.
      
      The fix is fairly obvious; just like the branch name is DWIMed when
      "git pull" merges from the 'master' branch without complaint on such
      a branch, the name of the branch the current branch builds upon
      needs to be DWIMed the same way.
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      05e73682
  9. 19 10月, 2013 1 次提交
  10. 10 9月, 2013 1 次提交
  11. 22 4月, 2013 4 次提交
    • J
      branch.c: Validate tracking branches with refspecs instead of refs/remotes/* · 41c21f22
      Johan Herland 提交于
      The current code for validating tracking branches (e.g. the argument to
      the -t/--track option) hardcodes refs/heads/* and refs/remotes/* as the
      potential locations for tracking branches. This works with the refspecs
      created by "git clone" or "git remote add", but is suboptimal in other
      cases:
      
       - If "refs/remotes/foo/bar" exists without any association to a remote
         (i.e. there is no remote named "foo", or no remote with a refspec
         that matches "refs/remotes/foo/bar"), then it is impossible to set up
         a valid upstream config that tracks it. Currently, the code defaults
         to using "refs/remotes/foo/bar" from repo "." as the upstream, which
         works, but is probably not what the user had in mind when running
         "git branch baz --track foo/bar".
      
       - If the user has tweaked the fetch refspec for a remote to put its
         remote-tracking branches outside of refs/remotes/*, e.g. by running
             git config remote.foo.fetch "+refs/heads/*:refs/foo_stuff/*"
         then the current code will refuse to use its remote-tracking branches
         as --track arguments, since they do not match refs/remotes/*.
      
      This patch removes the "refs/remotes/*" requirement for upstream branches,
      and replaces it with explicit checking of the refspecs for each remote to
      determine whether a given --track argument is a valid remote-tracking
      branch. This solves both of the above problems, since the matching refspec
      guarantees that there is a both a remote name and a remote branch name
      that can be used for the upstream config.
      
      However, this means that refs located within refs/remotes/* without a
      corresponding remote/refspec will no longer be usable as upstreams.
      The few existing tests which depended on this behavioral quirk has
      already been fixed in the preceding patches.
      
      This patch fixes the last remaining test failure in t2024-checkout-dwim.
      Signed-off-by: NJohan Herland <johan@herland.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      41c21f22
    • J
      checkout: Use remote refspecs when DWIMming tracking branches · fa83a33b
      Johan Herland 提交于
      The DWIM mode of checkout allows you to run "git checkout foo" when there
      is no existing local ref or path called "foo", and there is exactly _one_
      remote with a remote-tracking branch called "foo". Git will automatically
      create a new local branch called "foo" using the remote-tracking "foo" as
      its starting point and configured upstream.
      
      For example, consider the following unconventional (but perfectly valid)
      remote setup:
      
      	[remote "origin"]
      		fetch = refs/heads/*:refs/remotes/origin/*
      	[remote "frotz"]
      		fetch = refs/heads/*:refs/remotes/frotz/nitfol/*
      
      Case 1: Assume both "origin" and "frotz" have remote-tracking branches called
      "foo", at "refs/remotes/origin/foo" and "refs/remotes/frotz/nitfol/foo"
      respectively. In this case "git checkout foo" should fail, because there is
      more than one remote with a "foo" branch.
      
      Case 2: Assume only "frotz" have a remote-tracking branch called "foo". In
      this case "git checkout foo" should succeed, and create a local branch "foo"
      from "refs/remotes/frotz/nitfol/foo", using remote branch "foo" from "frotz"
      as its upstream.
      
      The current code hardcodes the assumption that all remote-tracking branches
      must match the "refs/remotes/$remote/*" pattern (which is true for remotes
      with "conventional" refspecs, but not true for the "frotz" remote above).
      When running "git checkout foo", the current code looks for exactly one ref
      matching "refs/remotes/*/foo", hence in the above example, it fails to find
      "refs/remotes/frotz/nitfol/foo", which causes it to fail both case #1 and #2.
      
      The better way to handle the above example is to actually study the fetch
      refspecs to deduce the candidate remote-tracking branches for "foo"; i.e.
      assume "foo" is a remote branch being fetched, and then map "refs/heads/foo"
      through the refspecs in order to get the corresponding remote-tracking
      branches "refs/remotes/origin/foo" and "refs/remotes/frotz/nitfol/foo".
      Finally we check which of these happens to exist in the local repo, and
      if there is exactly one, we have an unambiguous match for "git checkout foo",
      and may proceed.
      
      This fixes most of the failing tests introduced in the previous patch.
      Signed-off-by: NJohan Herland <johan@herland.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      fa83a33b
    • J
      t2024: Show failure to use refspec when DWIMming remote branch names · ec2764ee
      Johan Herland 提交于
      When using "git checkout foo" to DWIM the creation of local "foo" from some
      existing upstream "foo", we assume conventional refspecs as created by "git
      clone" or "git remote add", and fail to work correctly if the current
      refspecs do not follow the conventional "refs/remotes/$remote/*" pattern.
      Improved-by: NJonathan Nieder <jrnieder@gmail.com>
      Signed-off-by: NJohan Herland <johan@herland.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      ec2764ee
    • J
      t2024: Add tests verifying current DWIM behavior of 'git checkout <branch>' · 399e4a1c
      Johan Herland 提交于
      The DWIM mode of checkout allows you to run "git checkout foo" when there is
      no existing local ref or path called "foo", and there is exactly one remote
      with a remote-tracking branch called "foo". Git will then automatically
      create a new local branch called "foo" using the remote-tracking "foo" as
      its starting point and configured upstream.
      Improved-by: NJonathan Nieder <jrnieder@gmail.com>
      Signed-off-by: NJohan Herland <johan@herland.net>
      Signed-off-by: NJunio C Hamano <gitster@pobox.com>
      399e4a1c