0

Imagine for the sake of argument I've got a copy of a repo with 3 files, A B and C, with the original repo set as the upstream, with me pushing my own changes to a separate remote origin repo.

Let's now say I make a change to file C, and similarly the author makes a change to file B on the upstream. How would I pull the change to file B from upstream, whilst not also pulling the original C from upstream, as I want to keep my version of file C?

Cheers

P227
  • 57
  • 4
  • 1
    have a look at [this](https://stackoverflow.com/questions/16230838/is-it-possible-to-pull-just-one-file-in-git) – Daemon Painter Jan 13 '21 at 15:08
  • @P227, did the setup I suggested in the other question work for you? As long as there are no conflicts (you both change different files), you should be able to pull upstream safely to obtain latest changes from upstream. If git detects a conflict, git aborts pull and gives a warning. – Mohana Rao Jan 13 '21 at 17:54

1 Answers1

0

Repositories don't have files. Repositories have commits. The commits then have files, so you might say this is just a semantic quibble, but it's crucial to the answer, because the commits are package deals.

What you can do in this situation—note that I assume you made a commit with your updated file already—is:

  • obtain the commits from the other Git repository (git fetch);
  • use git merge to combine entire commits; and then
  • make a commit that mostly uses the merge result, but keeps your partciular file intact.

There are a bunch of minor tweaks you can use during this process. In particular, the middle step—use git merge—can be done with --no-commit, so that Git does the work of merging, but then stops before making the resulting merge commit. By stopping Git in the middle of the merge like this, you make Git act like it does when it stops due to a failure to perform the merge automatically. You're now in a state in which Git has an unfinished merge, and your job is to finish it. This gives you full control of what goes into the merge as the merge result.

Often, however, it may be better to just allow Git to make this merge as a normal everyday merge, then build a new commit after the merge that restores your one file. This produces a commit that, when checked out, has the same result as a modified merge, but leaves the actual merge in the history. (Remember, Git's commits are the history in the file. Any commit you make is history. A commit that you don't make—that you modify first, and then only make the modified commit—does not leave any traces, because the only history in the Git repository is the set of commits that you can find in the repository.1)

In other words, you can feel safe enough just running:

git fetch
git merge
git checkout <commit-hash> -- path/to/C
git commit

and if you like git pull you can use git pull to combine the fetch-and-merge via the convenience git pull command. (This assumes you have not configured git pull to run git rebase as its second command.)

The git checkout hash -- path command requires that you find some historic commit that contains the file in the form that you want. You can use git log to find it. If your Git is version 2.23 or later, you can use git restore instead of git checkout; the syntax is then:

git restore --staged --worktree --source=<hash> -- <path>

or if you prefer typing fewer characters:

git restore -SW --source=<hash> -- <path>

If you're using the git merge --no-commit method, you can still use the same kind of git checkout or git restore command to replace the file before using git merge --continue or git commit to complete the merge. Note that such a merge is called an "evil merge"; see Evil merges in git?.


1There are some technical exceptions to this involving both Git's reflogs, and "dangling" (unreferenced) commits. These aren't found with normal git log commands, but are findable for a while. But these eventually expire, leaving only the normal commits.

torek
  • 448,244
  • 59
  • 642
  • 775